hands making clay pot on brown wooden table
Photo by Anastasia Shuraeva on Pexels.com

Guía completa de accesibilidad para animación y movimiento: diseñar e implementar movimiento como “información cómoda”

Resumen (puntos clave primero)

  • Verbaliza el propósito y limita el movimiento a animaciones significativas que apoyen la comprensión, la atención y el feedback, no el adorno decorativo.
  • Diseña tiempo, distancia y dirección con cuidado, considerando mareo por movimiento, regulación de la atención y fotosensibilidad.
  • Respeta los ajustes del SO (p. ej., prefers-reduced-motion) y ofrece opciones de detener / reducir / alternativas.
  • Diseña cambios de estado, movimiento del foco y notificaciones con una expresión en tres capas: “visible + legible + tangible”.
  • Esta guía empaqueta fragmentos de implementación (CSS/JS), procedimientos de revisión/prueba y PDCA para operaciones en un solo lugar.

Lectores objetivo (concretos): diseñadores UI/UX, ingenieros frontend, PMs de producto, QA, marca/marketing, creadores de e-learning y educación
Nivel de accesibilidad: aspira a WCAG 2.1 AA (usa AAA cuando sea factible)


1. Introducción: el movimiento no es “decoración”, es la puntuación de tu interfaz

La animación hace más que dar carácter a una UI; ayuda a visualizar relaciones, expresar el tiempo y vincular causa con efecto. El exceso de movimiento y las transiciones llamativas pueden causar fatiga, distracción y malestar similar al mareo.
Esta guía trata el movimiento como parte de la capa de información, explicando un diseño e implementación que sean “cómodos y clarificadores” para todos. Palabras guía: mantenlo mínimo, con propósito y siempre detenable.


2. Principios del diseño de movimiento: alinea propósito, timing y espacio

2.1 Empieza verbalizando el propósito

  • Indicar flujo: muestra de dónde viene el contenido nuevo y a dónde va (p. ej., un panel lateral que entra).
  • Revelar jerarquía: expresa padre/hijo o frente/fondo mediante escala y profundidad (p. ej., un modal con superposición).
  • Feedback de estado: indica guardar/completar/error con un cambio breve (color, forma, icono, háptica).
  • Guiar la atención: atrae suavemente la mirada a la info necesaria (un realce ligero, una sola vez).
    → Si un movimiento no encaja en lo anterior, considera eliminarlo.

2.2 Timing: corto y predecible

  • Base sugerida: 200–300 ms; ajusta ±100 ms según peso/distancia de UI.
  • Escalona, no amontones: para múltiples elementos, un escalonado de 20–40 ms mejora la percepción.
  • Easing: para UI, ease-in-out; para énfasis, una curva tendente a desacelerar como cubic-bezier(.2,.8,.2,1) se siente suave.
  • No hagas esperar: evita animaciones largas e indeterminadas. Usa spinners + texto o skeletons para hacer visible el tiempo.

2.3 Espacio: distancia, dirección, escala

  • Mantén distancias cortas: recorridos largos inducen náusea. Prefiere fundidos suaves + deslizamientos cortos.
  • Consistencia direccional: menús que entran por la derecha deben salir por la derecha; tarjetas apiladas hacia arriba deben descartarse hacia arriba—mantén pares entrada/salida.
  • Poca escala: una microescala de 1.0 → 1.03 comunica “presionado”. Define transform-origin intencionalmente.

3. Respeta fisiología y diferencias: mareo, fotosensibilidad, control de la atención

  • Parallax y grandes paneos/zooms son grandes desencadenantes de mareo. Predetermina estático o mínimo.
  • Destellos a ≥3 Hz pueden suponer riesgo de convulsión. Mejor ninguno; si no, mantén <3 veces/seg y limita el contraste de luminancia.
  • Secuestros continuos de atención (sacudidas/pulsos constantes) sobrecargan a usuarios incl. con TDAH. Ofrece alternativas estáticas o control del usuario.
  • Para audio/háptica, ofrece siempre silencio, detener y controles de intensidad.

4. Respeta los ajustes del SO: prefers-reduced-motion es una “promesa”

Si el usuario elige “reducir movimiento” a nivel del SO, tu UI debe seguirlo.

/* Default (standard) */
.modal[open] { 
  animation: modal-in 240ms cubic-bezier(.2,.8,.2,1);
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(12px) scale(.98); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  .modal[open] { animation: none; transition: none; }
  .modal { transform: none !important; opacity: 1 !important; }
}
  • Tres niveles de reducción
    1. Detener (prioridad máxima).
    2. Suavizar (menor distancia/duración; sin bucles).
    3. Alternar (cambiar a señales sin movimiento como color, grosor, subrayado).
  • Ajustes del usuario > marca. Prioriza salud y enfoque sobre los adornos de marca.

5. “Detenable, ocultable, revisable”: UI con soberanía del usuario

  • Proporciona controles de Detener animación (a nivel de página o módulo).
  • Para regiones siempre en movimiento (tickers, carruseles, video autoplay), ofrece siempre detener/pausa/ocultar.
  • Para avisos importantes, no parpadees: usa texto + icono + color de forma redundante. Si se pierde, ofrece historial de toasts o centro de notificaciones.

Ejemplo: detener un ticker

<div class="ticker" role="region" aria-label="Latest updates">
  <button type="button" class="ticker__toggle" aria-pressed="false">Pause</button>
  <div class="ticker__track" aria-live="polite">…messages…</div>
</div>
<script>
const btn = document.querySelector('.ticker__toggle');
const track = document.querySelector('.ticker__track');
let running = true, timer = setInterval(roll, 4000);
btn.addEventListener('click', ()=>{
  running = !running;
  btn.setAttribute('aria-pressed', String(!running));
  btn.textContent = running ? 'Pause' : 'Resume';
  clearInterval(timer);
  if (running) timer = setInterval(roll, 4000);
});
function roll(){ if(!running) return; /* Swap to the next message */ }
</script>

6. Buenas prácticas por escena común

6.1 Contenido dentro/fuera (fundido + deslizamiento corto)

  • Lidera con fundido (evita parpadeos discontinuos; transiciona 0 → 1 suavemente).
  • Apoya con deslizamiento corto (8–16 px) para indicar origen.
  • Simetría: empareja entrar/salir (entrar = abajo→arriba, salir = arriba→abajo).
.card-enter { opacity: 0; transform: translateY(12px); }
.card-enter-active {
  opacity: 1; transform: translateY(0);
  transition: opacity 200ms, transform 200ms;
}

6.2 Enfoque y affordance de interacción

  • La visibilidad de enfoque (:focus-visible) debe ser clara mediante color + grosor + offset.
  • En clic/toque, usa sombra breve y microescala para comunicar pulsación.
:focus-visible { outline: 3px solid #FF9900; outline-offset: 3px; }
.button:active { transform: translateY(1px) scale(.99); }

6.3 Feedback (éxito / precaución / error)

  • Éxito: cambio de color + icono con una aparición suave y única.
  • Precaución: amarillos + icono; sin parpadeo.
  • Error: rojo + icono + háptica (si procede) y texto de remediación.
<div id="status" role="status" aria-atomic="true" class="sr-only"></div>
<script>
function saved(){ status.textContent = 'Save completed.'; }
function error(){ status.textContent = 'Save failed. Please check your network.'; }
</script>

6.4 Reordenar listas y drag-and-drop

  • Ofrece alternativas al arrastre (botones subir/bajar) para que usuarios de un dedo y teclado completen la tarea.
  • Durante el reordenamiento, usa movimientos cortos + sombra para comunicar solo el cambio de posición. Evita movimientos grandes o rotaciones.

7. No dependas solo del movimiento: diseña señales redundantes

  • Usa el trío color + forma + texto para transmitir significado (p. ej., error = rojo + marca de error + mensaje).
  • Proporciona texto para el contenido transmitido durante la animación (“Tienes 3 elementos nuevos”).
  • Evita depender solo de sonido / háptica / movimiento; combina canales visuales, auditivos y táctiles.

8. Arquitectura de implementación: prefiere CSS, minimiza JS

  • Prefiere transiciones/animaciones CSS: aprovecha rutas del compositor; céntrate en transform/opacity.
  • Deja JS para estado: mostrar/ocultar, ARIA, alternar clases—solo lógica.
  • Cancelabilidad: admite detener en cualquier momento vía animation-play-state o flags.
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReduced) document.documentElement.classList.add('motion-reduced');
.motion-reduced * { animation: none !important; transition: none !important; }

9. Modales/overlays: sé suave con la profundidad

  • Fondo: una superposición tranquila del 20–30%.
  • Escala de entrada mínima (p. ej., 1 → 1.02); mantén movimiento en Z sutil.
  • Incluye siempre focus trap + Esc para cerrar + devolver el foco al disparador. Incluso con movimiento mínimo, la completitud informativa es lo que cuenta.

10. Movimiento en gráficos, tutoriales y contenido educativo

  • Gráficos: mantén trazos animados cortos; escena por serie (p. ej., barras por grupo) para ayudar a la percepción. En pausa, muestra etiquetas estáticas.
  • Tutoriales: mueve solo el paso en foco; deja todo lo demás quieto. Limita el área en movimiento a ≤ 1/6 del viewport para reducir fatiga.
  • E-learning: combina con subtítulos, resúmenes y diagramas para que los usuarios comprendan sin ver el movimiento.

11. Criterios de revisión para proteger la calidad (diseño + implementación)

  1. Propósito: ¿Qué comunica este movimiento? ¿Puede eliminarse? ¿Puede reemplazarse por texto/señales estáticas?
  2. Salud: ¿Hay destellos/movimientos grandes/rotación? ¿Se detiene con prefers-reduced-motion?
  3. Predictibilidad: ¿Mismo componente = misma duración/easing? ¿Entrar/salir emparejados direccionalmente?
  4. Redundancia: ¿Además del movimiento, color/forma/texto transmiten significado?
  5. Operabilidad: ¿Teclado y lector de pantalla pueden percibir el estado? ¿Notificaciones vía role="status"?
  6. Rendimiento: ¿Te ciñes a transform/opacity sin sacudidas de layout? ¿Cerca de 60 fps?
  7. Control e historial: ¿Siempre hay detener/reanudar y revisión?

12. Prueba de humo de 5 minutos

  • Cambia el SO a Reducir movimiento y recorre pantallas clave: ¿se detienen las animaciones? ¿Se pierde información?
  • Usa solo teclado para flujos de modal/toast/pestañas: ¿anuncios y movimiento de foco coinciden?
  • Revisa historial de notificaciones y alternativas sin movimiento (color/negrita/subrayado).
  • En móvil, asegúrate de no usar deslizamientos grandes frecuentes.
  • DevTools performance: confirma mínimo Layout/Paint durante animaciones.

13. Ejemplo: toast accesible (con pausa e historial)

<div class="toaster" aria-live="polite" aria-atomic="true">
  <div class="toast" hidden role="status">
    <p class="toast__msg">Saved</p>
    <button class="toast__pause" aria-pressed="false">Pause</button>
    <button class="toast__close" aria-label="Close">×</button>
  </div>
  <details class="log">
    <summary>Notification history</summary>
    <ul class="log__list" aria-label="Notification history list"></ul>
  </details>
</div>
.toast[hidden]{ display:none; }
.toast{ 
  background:#111; color:#fff; border-radius:.75rem; padding:.75rem 1rem;
  box-shadow:0 6px 24px rgba(0,0,0,.2); max-width:28rem; 
  transform: translateY(8px); opacity:0;
  transition: opacity 180ms, transform 180ms;
}
.toast.show{ transform: translateY(0); opacity:1; }
@media (prefers-reduced-motion: reduce){
  .toast{ transition:none; transform:none; opacity:1; }
}
const box = document.querySelector('.toast');
const msg = document.querySelector('.toast__msg');
const log = document.querySelector('.log__list');
const pauseBtn = document.querySelector('.toast__pause');
const closeBtn = document.querySelector('.toast__close');
let timer, paused = false;

function notify(text){
  msg.textContent = text;
  box.hidden = false; box.classList.add('show');
  log.insertAdjacentHTML('afterbegin', `<li>${new Date().toLocaleTimeString()}: ${text}</li>`);
  clearTimeout(timer);
  timer = setTimeout(()=>{ if(!paused) hide(); }, 3000);
}
function hide(){ box.classList.remove('show'); setTimeout(()=> box.hidden = true, 200); }

pauseBtn.addEventListener('click',()=>{
  paused = !paused;
  pauseBtn.setAttribute('aria-pressed', String(paused));
  pauseBtn.textContent = paused ? 'Resume' : 'Pause';
});
closeBtn.addEventListener('click', hide);

// Example: call on save completion
// notify('Save completed.');

14. Caso de estudio: rescatar una expresión de marca “sobre-animada”

Antes

  • Fuerte parallax en el hero; fondo en movimiento constante.
  • Transiciones de sección con zooms grandes; carrusel en autoplay sin detener.
  • Alto bounce; usuarios con lector de pantalla informan “no puedo seguir la interfaz”.

Después

  • Sustituir parallax por imágenes estáticas + fundido suave.
  • Transiciones de sección con deslizamientos cortos + fundidos, y parada total bajo reducción de movimiento.
  • Carrusel con autoplay desactivado; añadir controles pausa/reproducir y ant./sig.
  • Resultados: tiempo en página +16%, finalización de scroll +22%, tickets de soporte (mareo/operabilidad) −78%.

15. Checklist (copiar/pegar para operaciones)

  • [ ] Propósito del movimiento definido; movimiento innecesario eliminado
  • [ ] Sin destellos/movimientos grandes/rotación, o son detenedores
  • [ ] prefers-reduced-motion implementado para detener/suavizar/alternar
  • [ ] Duración/easing/dirección consistentes por componente
  • [ ] Significado transmitido vía color/forma/texto, no solo movimiento
  • [ ] Notificaciones anunciadas (p. ej., role="status") y visibles en historial
  • [ ] Teclado y lector de pantalla utilizables/entendibles
  • [ ] Primero transform/opacity; sin “thrash” de layout
  • [ ] Controles de detener / pausar / reanudar disponibles
  • [ ] Pasa la prueba de humo de 5 minutos (SO reduce movimiento, teclado, historial, móvil)

16. Beneficios concretos por rol

  • Diseñadores UI/UX: el movimiento guiado por propósito une expresión y función; revisiones más fluidas.
  • Ingeniería frontend: CSS primero y JS mínimo producen implementaciones resistentes a regresiones y menos quejas gracias a reducción de movimiento.
  • PMs/directores: equilibrio entre marca y salud, mejorando rebote y CVR.
  • QA: verificación estable mediante checklist + prueba de humo.
  • Educación/e-learning: protege el foco del alumno, reduciendo carga cognitiva.
  • Usuarios: menos náusea, fatiga y omisiones—una experiencia más calmada y fiable.

17. Cierre: lo silencioso es amabilidad

  1. Elimina movimiento sin propósito. Para el necesario, mantenlo corto, cercano y predecible.
  2. Respeta reducción de movimiento del SO y ofrece siempre detener / suavizar / alternativas.
  3. Diseña señales redundantes (color/forma/texto) para que el significado no dependa del movimiento.
  4. Prefiere CSS y transform/opacity para suavidad y eficiencia.
  5. Ofrece detener, historial y anuncios para lector de pantalla para evitar pérdidas.
  6. Incorpora criterios de revisión y una prueba de 5 minutos en operaciones para elevar la calidad continuamente.

El movimiento debe ser una señal suave que guía. Que tu UI se siente a un lado del usuario y ofrezca un empujón ligero y oportuno solo cuando haga falta. Estoy contigo.


por greeden

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)