Animaciones CSS nativas (sin JS): hasta dónde puedes llegar solo con CSS
No siempre necesitas JavaScript para animar una interfaz. Con las animaciones nativas de CSS puedes resolver la mayoría de transiciones, hovers y efectos de scroll ligeros con menos código, mejor rendimiento y menos bugs. Y si necesitas un ejemplo real, tienes uno justo en el hero de mi portfolio.
Qué entendemos por animaciones CSS nativas
Cuando hablamos de animaciones CSS nativas nos referimos a todo lo que puedes hacer usando únicamente propiedades de CSS como de «transition» y de «animation», junto con de «@keyframes» y las nuevas APIs de animación ligadas al scroll o al estado del usuario, sin escribir una sola línea de JavaScript.
El navegador se encarga de calcular y ejecutar los frames de la animación, y tú solo describes los estados iniciales y finales. Esto reduce complejidad, evita tener que orquestar timelines en JS y suele producir un rendimiento más predecible en la mayoría de casos de UI.
Transiciones CSS: el nivel 1 de la animación nativa
Las transiciones son la forma más simple y limpia de animar algo en CSS. En lugar de definir toda una secuencia de keyframes, le dices al navegador que cada vez que cambie una propiedad, la anime en un tiempo determinado con una curva de easing concreta.
.button {
background-color: #111827;
color: white;
transform: translateY(0);
transition:
background-color .18s ease-out,
transform .18s ease-out,
box-shadow .18s ease-out;
}
.button:hover {
background-color: #0f766e;
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(15, 118, 110, .25);
}
Lo interesante de las transiciones es que no piensas en animaciones, piensas en estados. Cada vez que cambias de clase, pseudoestado o media query, el navegador interpola de uno a otro sin que tengas que montar lógica adicional.
Animaciones con @keyframes: cuando necesitas más control
Cuando ya no basta con animar de A a B, entran en juego los de «@keyframes». Aquí defines una secuencia de estados intermedios y la aplicas con propiedades como de «animation-name», de «animation-duration» o de «animation-iteration-count».
@keyframes fade-in-up {
0% {
opacity: 0;
transform: translateY(16px);
}
100% {
opacity: 1;
transform: translateY(0);
}}
.card {
opacity: 0;
animation: fade-in-up .4s ease-out forwards;
}
El patrón típico es aplicar la animación a determinados elementos cuando se añaden al DOM o cuando reciben una clase específica. En muchos casos esa clase ya te la pone el propio framework o el builder al abrir un modal o mostrar un dropdown.
Un ejemplo real: el hero de gloobs.com
El hero de gloobs.com es un buen caso práctico de hasta dónde puede llegar CSS puro cuando se usa con intención. La escena es un entorno urbano ilustrado con SVGs en capas, y cada elemento tiene su propia lógica de movimiento, sin una sola línea de JavaScript.
La estructura es sencilla: un contenedor con de>position: relative y de>overflow: hidden, y dentro todos los SVGs posicionados en absoluto, cada uno con su propia animación. El resultado es una escena viva y orgánica que el navegador renderiza de forma completamente nativa.
Nubes animadas
Las nubes cambian su opacidad de forma continua con una animación en loop infinito.
.nubes {position: absolute; z-index: 2; opacity: 50%;
animation-name: anima-nubes;
animation-duration: 3s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
@keyframes anima-nubes { 0% { opacity: 80$; } 50%{ opacity: 100%; } }
Edificios que salen del suelo
Los edificios del fondo aparecen por separado desde el suelo hasta su posición final.
.edificio1 {position: absolute; z-index: 3;
animation-name: anima-edificio1;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: 1; /*----cuántas veces quiero que se produzca la animación */
}
@keyframes anima-edificio1 {
from {
transform: translateY(200px);
}
to {
transform: translateY(0px);
}}
Por qué funciona esta escena sin JS
La clave está en que cada SVG es una capa independiente con posicionamiento absoluto dentro de un contenedor relativo. Cada elemento tiene su propio ritmo y su propio retraso, y el resultado es una escena que se siente viva sin que haya ningún script orquestándola. CSS gestiona todo el ciclo de animación y el navegador se encarga del renderizado de forma nativa.
Es también un ejemplo de cómo los SVGs son el formato ideal para este tipo de heroes animados: escalan sin pérdida de calidad, se pueden animar por capas desde CSS y tienen un peso muy inferior al de una imagen equivalente en PNG o un vídeo.
Scroll-driven animations: animar al hacer scroll sin JS
Durante años, si querías animar al hacer scroll necesitabas JavaScript. Ahora CSS incorpora de>animation-timeline: view(), que permite sincronizar la animación con la posición del elemento en la ventana, sin escuchar eventos ni añadir clases desde un script.
@keyframes fade-in-on-scroll {
0% {
transform: translateY(32px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}}
.fade-in-on-scroll {
animation: fade-in-on-scroll linear forwards;
animation-timeline: view();
animation-range: entry;
}
El soporte en navegadores modernos es bueno pero todavía no universal, así que conviene envolverlo en de>@supports para hacer progressive enhancement y dejar un fallback estático razonable.
@supports (animation-timeline: view()) {
.fade-in-on-scroll {
animation-timeline: view();
animation-range: entry;
}
}
Si quieres profundizar en esta API, la documentación de MDN sobre animation-timeline y los ejemplos de scroll-driven-animations.style son los mejores puntos de partida.
Buenas prácticas de rendimiento
Una animación nativa no es automáticamente rápida. La regla de oro es animar solo propiedades que pasen por el compositor de la GPU: principalmente de>transform y de>opacity.
- Evita animar «top», «left», «width», «height» o «margin»: fuerzan reflows y repaints costosos.
- Usa «transform, translate / scale / rotate» para mover o escalar sin recalcular el layout.
- Usa «will-change» solo en elementos muy concretos y quítalo cuando ya no sea necesario.
- Limita las animaciones simultáneas visibles en pantalla, especialmente en mobile.
Accesibilidad: respetar prefers-reduced-motion
Si usas animaciones de forma intensiva, tienes que respetar a los usuarios que han indicado en su sistema operativo que prefieren reducir el movimiento.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}}
A partir de ahí puedes afinar y desactivar solo las animaciones decorativas, dejando activas las que son realmente funcionales como un feedback de éxito o error muy breve.
Cuándo no basta con CSS y sí tiene sentido usar JS
No todo se puede o se debe resolver con CSS puro. En cuanto necesitas timelines complejos sincronizados, animaciones basadas en física, secuencias con lógica condicional o estados de aplicación, una librería como GSAP o Framer Motion sigue siendo más cómoda y controlable.
La regla práctica: usa CSS nativo para hovers, transiciones de entrada y salida, efectos de scroll ligeros y microinteracciones simples. Pasa a JS cuando la animación forme parte de la lógica de producto y necesites estados, callbacks y control fino del timeline.
Preguntas frecuentes
¿Son más rápidas las animaciones en CSS que en JavaScript?
Bien hechas, las animaciones CSS y JS pueden rendir de forma muy similar porque al final el navegador utiliza la misma tubería gráfica. La diferencia práctica es que con CSS te resulta más fácil ceñirte a «transform» y «opacity», que son las propiedades más baratas de animar.
¿Puedo hacer animaciones complejas de scroll solo con CSS?
Cada vez más. Propiedades como «animation-timeline: view()» permiten ligar una animación al progreso de scroll sin JS. Aun así, hoy por hoy conviene tratarlas como mejora progresiva y dar un fallback simple para navegadores que no las soportan todavía.
¿Es buena idea animar muchos elementos a la vez?
No. Aunque sea todo CSS, un exceso de movimiento simultáneo fatiga al usuario y puede afectar al rendimiento, especialmente en mobile. Es mejor concentrar las animaciones en puntos clave y dejar el resto de la interfaz tranquila.
¿Necesito JS para animar SVGs como en el hero de gloobs?
No, si cada SVG es una capa independiente y las animaciones son continuas o en loop. Con «@keyframes, transform-origin, animation-delay y overflow: hidden» en el contenedor tienes todo lo que necesitas para montar una escena animada compleja sin una sola línea de JavaScript.
