transition
Transiciones
Shorthand que anima suavemente los cambios de valor de una o más propiedades CSS cuando ocurre un cambio de estado, interpolando entre el valor inicial y el valor destino durante un tiempo definido.
transition es el shorthand que le dice al navegador cómo animar el cambio de valor de una propiedad CSS. Sin transition, cada vez que un elemento cambia de estado —por ejemplo al pasar el mouse con :hover o al recibir foco con :focus— el cambio es instantáneo: el elemento salta de un valor al otro sin ninguna suavidad. Con transition declarada, el navegador interpola automáticamente entre el valor inicial y el valor destino durante un tiempo definido, produciendo la animación fluida.
La propiedad agrupa cuatro sub-propiedades: qué se anima (transition-property), cuánto tarda (transition-duration), con qué curva de velocidad (transition-timing-function) y cuánto espera antes de arrancar (transition-delay). Juntas controlan por completo cómo se ve y se siente la transición. El shorthand transition permite declararlas todas en una sola línea, con un orden definido.
Un detalle crucial que define si la transición funciona bien en ambas direcciones: transition se declara siempre en el estado base del elemento, no en la pseudo-clase de estado. Si se declara solo en :hover, la transición anima el cambio al entrar al hover, pero al salir el elemento vuelve de golpe sin animación. En el estado base, la transición aplica a cualquier cambio de valor en esa propiedad, sea por :hover, :focus, :active o por cambio de clase con JavaScript.
Cómo funciona
Cuando el navegador detecta que el valor de una propiedad CSS cambia (porque se activó un :hover, se agregó una clase, etc.), revisa si hay una transition declarada para esa propiedad en el elemento. Si la hay, en lugar de aplicar el nuevo valor de forma instantánea, calcula frames intermedios interpolando entre el valor inicial y el valor destino durante la duración especificada, siguiendo la curva de la timing-function.
La interpolación solo es posible con propiedades que tienen valores numéricos interpolables: colores (entre dos colores CSS), longitudes (width, height, padding, coordenadas), opacidad y transform. Propiedades discretas como display no se pueden interpolar — el cambio de display: none a display: block es siempre instantáneo aunque haya una transition declarada.
Un gotcha importante: la transition siempre aplica al cambio de valor que se produce. Si el estado inicial no tiene un valor explícito para la propiedad animada, el navegador asume el valor inicial de CSS (el 0 o none correspondiente), lo que puede producir transiciones inesperadas. Declará siempre el valor inicial en el estado base.
Sintaxis
/* Shorthand completo: propiedad duración timing-function delay */
.boton {
transition: opacity 0.3s ease-in-out 0s;
}
/* Forma mínima más común (delay omitido → 0s) */
.boton {
transition: opacity 0.3s ease-in-out;
}
/* Múltiples propiedades separadas por coma */
.tarjeta {
transition:
background-color 0.2s ease,
transform 0.3s ease-out;
}
/* Animar todas las propiedades (con precaución) */
.elemento {
transition: all 0.3s ease;
}
El orden dentro del shorthand es: propiedad → duración → timing-function → delay. El único par que puede generar ambigüedad es cuando se escriben dos tiempos: el primero siempre es la duración (transition-duration) y el segundo siempre es el delay (transition-delay).
Sub-propiedades en detalle
transition-property
Define qué propiedad CSS se va a animar cuando cambie su valor. Acepta el nombre de cualquier propiedad animable, la keyword all o none.
.boton {
transition-property: opacity; /* solo opacidad */
transition-property: background-color; /* solo color de fondo */
transition-property: opacity, transform; /* dos propiedades */
transition-property: all; /* todas las animables */
transition-property: none; /* desactiva transiciones */
}
Valor por defecto: all.
Cuándo usar cada forma:
- Nombrar propiedades específicas es la práctica recomendada: solo anima lo que querés y el navegador puede optimizar mejor.
alles cómodo para prototipos pero peligroso en producción: puede animar propiedades que no querías (por ejemplobox-shadowsi cambia en:hover), y dificulta el diagnóstico de bugs visuales.
Gotcha: transition-property: all puede transicionar propiedades que no se ven afectadas por el estado de hover pero sí cambian por herencia o cascada, generando animaciones inesperadas. Cuando tenés comportamiento extraño, cambiá all por propiedades específicas como primer paso de diagnóstico.
transition-duration
Define cuánto tiempo tarda la transición de inicio a fin. Es la sub-propiedad más importante: sin ella (o con el valor 0s), no hay transición visible.
.boton {
transition-property: opacity;
transition-duration: 0.3s; /* 300 milisegundos */
transition-duration: 300ms; /* equivalente */
transition-duration: 0s; /* sin transición (default) */
}
Valor por defecto: 0s — sin duración, el cambio es instantáneo.
CSS acepta segundos (s) y milisegundos (ms). 0.3s y 300ms son equivalentes. La unidad es obligatoria incluso para el cero: 0 sin unidad es inválido en este contexto y la propiedad se ignora en silencio.
Para animaciones de UI el rango natural es 0.15s–0.4s. Menos de 100ms es difícil de percibir; más de 500ms puede sentirse lento para interacciones cotidianas como botones.
transition-timing-function
Controla la curva de velocidad de la transición: cómo cambia la velocidad a lo largo del tiempo.
.boton {
transition-timing-function: ease; /* lento-rápido-lento (default) */
transition-timing-function: linear; /* velocidad constante */
transition-timing-function: ease-in; /* arranca lento, termina rápido */
transition-timing-function: ease-out; /* arranca rápido, termina lento */
transition-timing-function: ease-in-out; /* lento en ambos extremos */
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); /* personalizada */
transition-timing-function: steps(4, end); /* saltos discretos */
}
Valor por defecto: ease.
| Valor | Comportamiento | Cuándo usarlo |
|---|---|---|
ease | Arranca lento, acelera y frena suave | UI genérica, la mayoría de los casos |
linear | Velocidad constante sin aceleración | Barras de progreso, spinners, rotaciones |
ease-in | Arranca lento, termina rápido | Elementos que salen de pantalla |
ease-out | Arranca rápido, termina lento | Elementos que entran a pantalla |
ease-in-out | Lento al inicio y al final | Hover de UI — suaviza ambos extremos |
cubic-bezier() | Curva totalmente personalizada | Animaciones con carácter propio |
steps() | Animación en saltos discretos | Sprites, contadores animados |
Todos los keywords son atajos de cubic-bezier(). No es necesario conocer la función matemática para usarlos; las herramientas de DevTools permiten editarlas visualmente.
transition-delay
Define cuánto tiempo espera el navegador antes de iniciar la transición después de que se activa el cambio de estado.
.boton {
transition-property: opacity;
transition-duration: 0.3s;
transition-delay: 0s; /* sin espera (default) */
transition-delay: 0.1s; /* espera 100ms antes de arrancar */
transition-delay: -0.1s; /* arranca "a mitad" de la transición */
}
Valor por defecto: 0s — arranca inmediatamente.
Un delay positivo pequeño (0.05s–0.1s) puede mejorar la experiencia en elementos pequeños, evitando que la transición se dispare por pasar el cursor de refilón.
Los delays negativos tienen un comportamiento especial: hacen que la transición arranque como si ya hubiera pasado ese tiempo. Con transition-delay: -0.15s y transition-duration: 0.3s, la animación empieza desde el 50% del recorrido, útil en secuencias coordinadas.
Regla de expansión del shorthand
El shorthand transition sigue el orden fijo: propiedad duración timing-function delay. La única ambigüedad posible es entre los dos tiempos: el primer tiempo es siempre transition-duration, el segundo es siempre transition-delay.
/* Desglose explícito */
.elemento {
transition: opacity 0.3s ease-in-out 0.1s;
/* ↑ property ↑ duration ↑ timing ↑ delay */
}
/* Equivalente con longhands */
.elemento {
transition-property: opacity;
transition-duration: 0.3s;
transition-timing-function: ease-in-out;
transition-delay: 0.1s;
}
Para múltiples transiciones, cada grupo se separa por coma. Cada grupo puede tener sus propios valores:
.tarjeta {
transition:
background-color 0.2s ease,
transform 0.3s ease-out 0.05s;
}
Si transition-property lista más propiedades que las que hay en transition-duration (u otras sub-propiedades), los valores se repiten cíclicamente:
/* duration 0.2s aplica a opacity; 0.4s aplica a transform */
.elemento {
transition-property: opacity, transform;
transition-duration: 0.2s, 0.4s;
}
Ejemplo realista
/* Botón con transición de opacidad — eficiente y sutil */
.boton {
background-color: #2563eb;
color: #fff;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.5rem;
cursor: pointer;
transition: opacity 0.3s ease-in-out;
}
.boton:hover {
opacity: 0.75;
}
/* Tarjeta con elevación — la forma recomendada para hover de tarjetas */
.tarjeta {
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
transition: transform 0.25s ease-out;
}
.tarjeta:hover {
transform: translateY(-6px);
}
/* Con prefers-reduced-motion: respeto de accesibilidad */
@media (prefers-reduced-motion: reduce) {
.boton, .tarjeta {
transition: none;
}
}
El ejemploSandbox de esta entrada incluye ambos ejemplos ejecutables — pasá el mouse para ver las transiciones en vivo.
Cuándo usar qué
| Situación | Recomendación |
|---|---|
| Hover de botón o enlace | transition: opacity 0.2s ease-in-out |
| Elevación de tarjeta al hover | transition: transform 0.25s ease-out |
| Cambio de color de fondo | transition: background-color 0.2s ease |
| Varias propiedades coordinadas | Lista con coma, cada una con su duración |
| Prototipos rápidos | transition: all 0.3s ease (pero revisá en producción) |
| El elemento entra a escena | ease-out — arranca fuerte, frena suave |
| El elemento sale de escena | ease-in — parte despacio, sale rápido |
| Progreso, loaders | linear — velocidad constante |
Notas, gotchas e interacción
- La transición va en el estado base, no en
:hover. El error más común es escribirtransitiondentro de.boton:hover. Funciona al entrar al hover, pero al salir el elemento vuelve de golpe sin animación. En el estado base, la transición aplica en ambas direcciones: al cambiar el estado y al volver. Ver pseudo-clases para el contexto de:hover/:focus/:active. - Sin
transition-durationno hay transición. El valor por defecto es0s, lo que hace el cambio instantáneo aunque la propiedad esté declarada. Es el gotcha silencioso más frecuente: se declaratransition: opacity ease-in-outsin duración y no pasa nada. Siempre incluí la duración. transition: alles cómodo pero peligroso. Transiciona cualquier propiedad que cambie, incluyendo las que no querías animar. Puede activar transiciones inesperadas (por ejemplo, si el:hoverhereda un cambio deoutlineobox-shadow), y dificulta la optimización del navegador. En producción, nombrá las propiedades específicas.- No toda propiedad es transicionable. Solo las que tienen valores interpolables: colores, longitudes, números,
opacity,transform. La propiedaddisplayno transiciona — undisplay: noneaparece o desaparece de golpe. Para ocultar con transición, usáopacity: 0junto convisibility: hidden(opointer-events: none), nodisplay: none. opacityytransformson las propiedades más eficientes para transicionar. El navegador las compone en la GPU sin recalcular el layout ni repintar. Propiedades comowidth,height,topoleftfuerzan un layout completo en cada frame (potencial jank). Ver opacity para el detalle.prefers-reduced-motiones la práctica responsable. Algunos usuarios configuran su sistema operativo para reducir el movimiento (epilepsia fotosensible, mareos, preferencia personal). Un@media (prefers-reduced-motion: reduce) { transition: none; }respeta esa preferencia. Ver media queries para el contexto de las media features de preferencia de usuario.
Vigente / legacy
transition es Baseline widely — soportada en todos los navegadores modernos sin ninguna restricción. Los prefijos vendor -webkit-transition, -moz-transition y -o-transition fueron necesarios entre 2009 y 2013, pero hoy son historia y no deben usarse. No hay forma legacy relevante.
La sintaxis de steps() para animaciones en pasos discretos es igualmente amplia. cubic-bezier() personalizado también es Baseline widely. No hay casos de degradación a considerar en proyectos modernos.
Ejemplo ejecutable