Tema representativo de entrevista

Entrevista frontend: ¿Cómo usar CSS @starting-style para transiciones de entrada confiables?

FrontendIntermedio
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Un elemento dialog oculto con `display: none` debería aparecer con un fundido y escalado al abrirse, y realizar una transición de salida al cerrarse. ¿Cómo usarías CSS @starting-style, transition-behavior y las características de top-layer mientras mantienes la compatibilidad con navegadores antiguos, la accesibilidad y el comportamiento de prefers-reduced-motion?

1. Pregunta y contexto

Una página de administración tiene un elemento dialog y un menú popover. El diseño requiere transiciones de entrada y salida sin que JavaScript cambie estilos en cada fotograma; los navegadores antiguos deben seguir siendo utilizables y los usuarios que reducen el movimiento no deben ser forzados a una animación intensa. Explica el límite de @starting-style, la división entre CSS y scripts, el comportamiento de reserva (fallback) y la validación.

2. Qué evalúa el entrevistador

  • Saber que una primera actualización de estilo no tiene un estilo computado previo para una transición y que @starting-style proporciona un punto de partida.
  • Distinguir entre entrada y salida, display discretos, elementos en el top-layer y transiciones ordinarias de opacity/transform.
  • Mantener los scripts responsables de los cambios de estado mientras la plataforma gestiona el foco, Escape, la semántica y la preferencia de movimiento.
  • Diseñar comprobaciones de capacidades, condiciones de carrera por cancelación, movimiento reducido y verificación en navegadores reales en lugar de simplemente pegar CSS.

3. Preguntas para aclarar primero

  1. ¿Es un elemento dialog, un popover o un nodo ordinario? El comportamiento del top-layer y del foco predeterminado cambia el diseño.
  2. ¿Debe el nodo permanecer hasta que termine la animación de salida o volverse no interactivo de inmediato? Esto modifica display y los límites de eventos.
  3. ¿Debería un navegador antiguo mostrar un resultado estático, cambiar instantáneamente o coincidir píxel por píxel?
  4. Con prefers-reduced-motion, ¿debería reducirse la duración, desaparecer el desplazamiento o eliminarse todas las transiciones?

4. Una respuesta de 30 segundos

Dejaría que el estado y la semántica de HTML sean dueños de la visibilidad, el foco y el cierre, mientras que CSS se encarga de las transiciones visuales reversibles. Usa @starting-style para proporcionar valores de opacidad y transformación antes de la primera actualización visible; usa allow-discrete o una ruta de compatibilidad para display de modo que el elemento siga siendo dibujable durante la transición. La salida necesita un estado final explícito y un ciclo de vida en el DOM, no solo una regla de inicio. Los navegadores antiguos cambian instantáneamente, los usuarios con movimiento reducido obtienen una animación más corta o nula, y las pruebas de teclado, lector de pantalla y condiciones de carrera validan el resultado.

5. Respuesta paso a paso

Paso 1: Definir los límites de estado y semántica

dialog.showModal(), el estado abierto de un popover y la alternancia de una clase tienen semánticas diferentes. Asegúrate de que un control cerrado no pueda recibir foco, que un control abierto reciba un objetivo de foco adecuado y que los eventos de Escape y cancelación funcionen. La animación refleja el estado; no debe definir el estado de accesibilidad. Los scripts deben llamar a la API de la plataforma o alternar un atributo de estado, no simular la visibilidad con temporizadores.

Paso 2: Crear un punto de partida para la entrada

En una primera actualización de estilo, el navegador puede no tener un estilo previo, por lo que una transición ordinaria carece de origen. Coloca los valores iniciales en @starting-style:

css
.dialog {
  opacity: 1;
  transform: scale(1);
  transition: opacity 180ms ease, transform 180ms ease;
}

@starting-style {
  .dialog[open] {
    opacity: 0;
    transform: scale(0.96);
  }
}

La regla de inicio se aplica a la primera actualización de estilo, no como un estado cerrado persistente. Si un componente se inserta nuevamente, el hecho de que la entrada se ejecute depende de su ciclo de vida y de la operación en el DOM; no asumas que cada alternancia de atributo invoca la regla de inicio.

Paso 3: Manejar propiedades discretas y la salida

display no se puede interpolar como la opacidad. Mantén el elemento dibujable durante la transición y cámbialo a un estado no disponible en un punto discreto; los navegadores compatibles pueden usar transition-behavior: allow-discrete, con una alternativa instantánea en otros casos. Mantén un nodo que se está cerrando hasta que se complete la transición o deja que el componente nativo sea dueño de su ciclo de vida; no adivines con un setTimeout fijo. Filtra transitionend por propiedad y maneja cancelaciones, aperturas repetidas y desmontajes.

Paso 4: Considerar el top layer y la interacción

Los diálogos y popovers en el top layer cambian el apilamiento, el backdrop y el comportamiento del foco. Define las transiciones del contenido y de ::backdrop por separado, luego prueba clics externos, Escape, bloqueo de desplazamiento y múltiples instancias. Los scripts deben mantener las llamadas a la API, la cancelación y los callbacks de negocio; @starting-style no reemplaza una máquina de estados de JavaScript.

Paso 5: Respetar la preferencia de movimiento y verificar

Bajo @media (prefers-reduced-motion: reduce), elimina el movimiento, reduce la duración o deshabilita las transiciones manteniendo la retroalimentación de estado. Prueba la primera apertura, reaperturas, alternancias rápidas, navegación durante el cierre, fallos de script, navegadores antiguos, teclado, lectores de pantalla, backdrop y retorno del foco. Monitorea cancelaciones, transiciones interrumpidas, residuos de nodos ocultos y regresiones de accesibilidad en lugar de limitarte a revisar capturas de pantalla.

6. Respuesta modelo

Comenzaría con la semántica: las API nativas de dialog o popover junto con una pequeña cantidad de script controlan el estado abierto, el foco y Escape; CSS solo presenta ese estado. Una transición normal carece de un estilo previo en la primera actualización, por lo que @starting-style proporciona los orígenes de opacidad y transformación. display es discreto, así que utiliza el comportamiento de transición discreta compatible o cambia instantáneamente en navegadores antiguos. Mantén un nodo que se cierra hasta que termine la transición en lugar de adivinar con un temporizador. Define el contenido del top-layer y el backdrop por separado, y luego prueba con movimiento reducido, alternancias rápidas, teclado y lectores de pantalla.

7. Errores comunes

  • Tratar la regla de inicio como el estado cerrado: Las alternancias posteriores tendrán el estado incorrecto; separa los valores persistentes, de entrada y finales de cierre.
  • Aplicar una transición ordinaria de opacidad a display: El elemento desaparece demasiado pronto o sigue siendo interactivo; utiliza el comportamiento discreto y un fallback.
  • Eliminar nodos con setTimeout: La configuración del usuario, la cancelación o los fotogramas perdidos desincronizan el ciclo de vida; usa un evento de transición válido o el ciclo de vida nativo.
  • Dejar que la animación controle el foco y la semántica: La tecnología asistencial sigue el tiempo visual; establece primero el estado en HTML/API.
  • Ignorar el movimiento reducido: Se infringe la preferencia; reduce o elimina el movimiento manteniendo una retroalimentación de estado clara.

8. Preguntas de seguimiento y respuestas

Pregunta de seguimiento 1: ¿Por qué una transición ordinaria es insuficiente para el primer fundido de entrada (fade-in)?

Antes de la primera actualización de estilo, puede que no haya un estilo computado previo para comparar, por lo que el navegador aplica el valor final directamente. @starting-style suministra ese origen; no define cada estado de cierre posterior.

Pregunta de seguimiento 2: ¿Cómo puede participar display en la salida?

Mantén el elemento dibujable durante la transición y cambia display en un punto discreto. Usa allow-discrete donde sea compatible y cierra instantáneamente en el resto, asegurando al mismo tiempo que un elemento cerrado no pueda recibir foco ni interacción de puntero.

Pregunta de seguimiento 3: ¿Qué sucede durante cambios rápidos de abrir-cerrar-abrir?

El estado puede revertirse antes de que finalice la transición anterior. Cancela el trabajo de animación obsoleto, lee el estado actual y permite que el estado más nuevo sea el único confirmado; no ejecutes dos temporizadores que eliminen y reabran el nodo de forma independiente. Prueba clics alternados, Escape y el desmontaje por navegación.

Fuentes públicas

Preguntas relacionadas