Animaciones y transiciones
Las animaciones bien hechas no son decoración — son feedback. Cuando un botón cambia de color al pasar el ratón, cuando una notificación entra deslizándose, cuando un formulario vibra al enviarse con un error. El movimiento le dice al usuario "algo ha pasado". Pero si te pasas, te pasa como con las especias: en su punto está delicioso, demasiado arruina el plato.
Transiciones: el cambio suave
Las transiciones animan el cambio entre dos estados de una propiedad CSS:
.btn {
background: #ff6b35;
color: #fff;
padding: 0.75rem 1.5rem;
border-radius: 8px;
border: none;
cursor: pointer;
/* Transición: qué propiedad, duración, curva de aceleración */
transition: background 0.2s ease;
}
.btn:hover {
background: #e55a2b;
}
/* Sin transition: el cambio es instantáneo (feo, brusco)
Con transition: el color cambia suavemente en 0.2 segundos */
Múltiples transiciones
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 12px;
transform: translateY(0);
box-shadow: var(--shadow-sm);
/* Animar varias propiedades */
transition:
transform 0.2s ease,
box-shadow 0.2s ease,
border-color 0.2s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
border-color: var(--color-brand);
}
Evita
transition: all 0.3s. Anima solo las propiedades que necesitas.allpuede causar transiciones inesperadas y problemas de rendimiento.
Funciones de temporización
.ejemplo {
/* Velocidad constante */
transition-timing-function: linear;
/* Empieza lento, acelera, frena (por defecto) */
transition-timing-function: ease;
/* Empieza rápido, frena al final — sensación natural */
transition-timing-function: ease-out;
/* Empieza lento, termina rápido */
transition-timing-function: ease-in;
/* Curva personalizada — útil para rebotes */
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}
Para la mayoría de interacciones UI, ease-out es la mejor opción. El cambio es rápido al principio y frena suavemente al final, que es como se mueven las cosas en el mundo real.
Transform: mover, escalar, rotar
transform es la propiedad estrella para animaciones porque el navegador la optimiza con la GPU — es muy fluida:
.card:hover {
/* Mover */
transform: translateY(-4px); /* Arriba */
transform: translateX(10px); /* Derecha */
transform: translate(-50%, -50%); /* Centrar absoluto */
/* Escalar */
transform: scale(1.05); /* 5% más grande */
transform: scale(0.95); /* 5% más pequeño (efecto de clic) */
/* Rotar */
transform: rotate(3deg); /* Girar 3 grados */
/* Combinar */
transform: translateY(-4px) scale(1.02);
}
Rendimiento: las propiedades más baratas de animar son
transformyopacity. Estas no causan repaint del layout. Evita animarwidth,height,margin,padding,top,left— son costosas y causan janks (saltos visuales).
@keyframes: animaciones personalizadas
Las transiciones van de A a B. Las animaciones con @keyframes pueden tener pasos intermedios, repetirse y controlarse con más detalle:
/* Definir la animación */
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Aplicarla */
.card {
animation: fade-in 0.5s ease-out;
}
Animación con pasos intermedios
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgb(255 107 53 / 40%);
}
50% {
transform: scale(1.05);
box-shadow: 0 0 0 10px rgb(255 107 53 / 0%);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgb(255 107 53 / 0%);
}
}
.notification-badge {
animation: pulse 2s ease-in-out infinite;
}
Propiedades de animación
.elemento {
/* Shorthand: name duration timing-function delay iteration-count direction fill-mode */
animation: fade-in 0.5s ease-out 0.2s 1 normal forwards;
/* O separadas */
animation-name: fade-in;
animation-duration: 0.5s;
animation-timing-function: ease-out;
animation-delay: 0.2s; /* Espera antes de empezar */
animation-iteration-count: 1; /* 1 vez, o infinite */
animation-direction: normal; /* normal, reverse, alternate */
animation-fill-mode: forwards; /* Mantiene el estado final */
}
animation-fill-mode: forwards es clave: sin ella, el elemento vuelve a su estado original cuando termina la animación. Con forwards se queda en el estado final.
Animaciones escalonadas
Para que una lista de elementos aparezca uno detrás de otro:
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
opacity: 0;
animation: slide-up 0.4s ease-out forwards;
}
/* Cada card se retrasa un poco más */
.card:nth-child(1) { animation-delay: 0.1s; }
.card:nth-child(2) { animation-delay: 0.2s; }
.card:nth-child(3) { animation-delay: 0.3s; }
.card:nth-child(4) { animation-delay: 0.4s; }
/* O con variables CSS para hacerlo dinámico */
.card {
animation-delay: calc(var(--index) * 0.1s);
}
<div class="card" style="--index: 0">Card 1</div>
<div class="card" style="--index: 1">Card 2</div>
<div class="card" style="--index: 2">Card 3</div>
<div class="card" style="--index: 3">Card 4</div>
Animaciones con scroll
CSS moderno permite vincular animaciones al scroll sin JavaScript:
/* Barra de progreso de lectura */
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: var(--color-brand);
transform-origin: left;
animation: grow-width linear;
animation-timeline: scroll();
}
@keyframes grow-width {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* Elementos que aparecen al entrar en el viewport */
.reveal {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
Rendimiento y accesibilidad
/* SIEMPRE respeta la preferencia del usuario */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Reglas de oro para animaciones:
- Solo anima
transformyopacitypara rendimiento óptimo. - Duración corta: 0.15-0.3s para interacciones UI, 0.3-0.5s para apariciones.
- Respeta
prefers-reduced-motion. Siempre. - Menos es más: si todo se mueve, nada destaca.
- La animación debe tener propósito: feedback, guiar la atención o indicar un cambio de estado.
Anima la interfaz de Vinyl Paradise
Añade animaciones y transiciones a la tienda de vinilos:
- Transición suave en los botones: cambio de
backgroundytransform: scale(0.98)en:active - Cards que suben ligeramente en hover con
translateY(-4px)y sombra más pronunciada - Animación
@keyframesde entrada para las cards confade-in+translateY - Entrada escalonada: cada card se retrasa con
--indexycalc() - Una barra de progreso de scroll fija arriba con
animation-timeline: scroll() - Un badge "Nuevo" con animación
pulseinfinita y sutil - Bloque
@media (prefers-reduced-motion: reduce)que desactiva todas las animaciones
lightbulb Pistas
Empieza por las transiciones (hover/active) que son las más simples. Luego añade los keyframes de entrada. Para la barra de scroll, usa position: fixed con top: 0 y z-index alto. Recuerda: solo anima transform y opacity para rendimiento.