· 8 min
prefers-reduced-motion: mantener carácter sin castigar al usuario
- CSS
- Accesibilidad
- UI
- Motion
No hace falta elegir entre una UI viva y una UI accesible.
prefers-reduced-motion no significa dejar todo inmóvil: significa reducir el movimiento que puede marear, distraer o fatigar.
La clave es sencilla: menos desplazamiento, menos sorpresa y más claridad de estado.
1. Qué indica realmente prefers-reduced-motion
Cuando el sistema operativo activa esta preferencia, el usuario está diciendo algo bastante concreto:
- evita animaciones intensas;
- reduce parallax, zooms, giros y desplazamientos largos;
- no conviertas cada cambio de pantalla en una coreografía.
No es una petición estética. Es una preferencia de confort y accesibilidad.
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Esta base sirve para cortar excesos globales, pero no debería ser tu única estrategia.
2. Reducir no es eliminar
Un error común es apagar toda transición y dejar la interfaz brusca. Eso también empeora la experiencia.
La mejor alternativa es sustituir movimiento amplio por feedback corto y estable:
- cambia transform: translateY() por opacidad;
- cambia escalados agresivos por variaciones sutiles de color o sombra;
- cambia entradas desde fuera de pantalla por apariciones directas.
.card {
transition: transform 180ms ease, box-shadow 180ms ease, opacity 180ms ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: box-shadow 120ms ease, opacity 120ms ease;
}
.card:hover {
transform: none;
box-shadow: 0 0 0 2px rgba(14, 165, 233, 0.22);
}
}
La tarjeta sigue respondiendo. Simplemente deja de "saltar".
Hover con desplazamiento y sombra amplia.
Tarjeta interactiva
La jerarquía visual se mantiene, pero el recorrido desaparece cuando reduces movimiento.
3. Qué patrones conviene recortar primero
No toda animación molesta por igual. Normalmente los peores casos son:
- scroll suave obligatorio en páginas largas;
- menús o modales que cruzan toda la pantalla;
- fondos con parallax;
- loaders infinitos con pulsos o rebotes muy visibles;
- carruseles automáticos.
Si quieres priorizar, empieza por los movimientos grandes en eje X/Y y por las animaciones continuas.
4. Ejemplo de modal con alternativa reducida
Versión habitual: overlay con fade y panel con desplazamiento vertical. Versión reducida: solo fade corto.
.modal-overlay {
animation: overlay-in 180ms ease-out;
}
.modal-panel {
animation: modal-slide 220ms ease-out;
}
@keyframes overlay-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes modal-slide {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
.modal-panel {
animation: overlay-in 120ms linear;
}
}
El estado visual cambia igual, pero sin recorrido innecesario.
En lugar del modal genérico de siempre, aquí la comparación usa un panel contextual sobre una preview editorial.
Editorial preview
Landing hero refresh
Nueva propuesta visual con CTA principal, bloque editorial y un lateral con métricas de lectura.
Panel contextual
Este patrón muestra mejor la diferencia entre entrada lateral y fade reducido sin parecer una maqueta genérica.
5. JavaScript y scroll: donde más ruido se cuela
Muchas interfaces respetan CSS pero rompen la preferencia desde JavaScript.
Pasa con scrollIntoView, librerías de reveal o animaciones al hacer route change.
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
target.scrollIntoView({
behavior: reduceMotion ? "auto" : "smooth",
block: "start",
});
Si usas una librería de animación, la regla es la misma: desactiva timelines complejos y conserva solo cambios de estado mínimos.
6. En React o Next, mejor decidir una vez
Si la app tiene muchos componentes animados, centraliza la preferencia y úsala como condición.
const reduceMotion = useMemo(
() => window.matchMedia("(prefers-reduced-motion: reduce)").matches,
[]
);
const menuClassName = reduceMotion
? "opacity-100 transition-opacity duration-100"
: "translate-y-0 opacity-100 transition-all duration-200";
No necesitas duplicar toda la UI. Solo separar qué animaciones son decorativas y cuáles comunican estado.
7. El movimiento útil sí puede quedarse
Hay animaciones que ayudan:
- un foco visible que aparece con suavidad;
- un toast que hace fade corto;
- un acordeón que evita saltos bruscos de layout;
- un cambio de color o elevación al interactuar.
La pregunta no es "¿hay movimiento?". La pregunta correcta es "¿este movimiento explica algo o solo adorna?".
8. Checklist rápida
- Desactiva
smooth scrollcuando el usuario pide reducción. - Sustituye desplazamientos largos por opacidad, color o sombra.
- Recorta parallax, autoplay y animaciones continuas.
- Revisa también JavaScript y librerías, no solo CSS.
- Conserva feedback útil y elimina movimiento decorativo.
Conclusión
Una interfaz no pierde personalidad por reducir movimiento. Pierde calidad cuando confunde energía visual con buena experiencia.
prefers-reduced-motion obliga a diseñar mejor:
más intención, menos espectáculo y estados más claros.
Si tu UI sigue funcionando bien cuando recortas movimiento, probablemente estaba bien resuelta desde el principio.
