View Transitions API: estado y navegación
Coordina document.startViewTransition con el estado del DOM, ready y finished, historial, abortos y alternativas para reducir movimiento.
BotBrowser Team
Quieres la documentación estructurada de Plataforma?
Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.
View Transitions API puede hacer continua una navegación dentro del mismo documento, pero la animación es solo una parte del contrato. BotBrowser puede repetir un escenario autorizado y registrar estados acotados del DOM, del ciclo de vida y de las preferencias; no puede añadir la API a un navegador que no la implemente ni certificar lo que anuncia una tecnología de asistencia. Combina esta guía con historial y privacidad de Navigation API y preferencias de media queries y control del usuario cuando la transición también decide una ruta o el movimiento permitido.
TL;DR
- Llama a
document.startViewTransition()alrededor de una única actualización síncrona del DOM y haz idempotente el callback. - Trata
transition.readycomo el punto en que los pseudo-elementos capturados pueden animarse;transition.finishedes la fase visual estable. - Una actualización rechazada o una transición abortada es una alternativa controlada, no una prueba de que falló la ruta. El nuevo DOM debe seguir siendo utilizable.
- Actualiza el estado de la aplicación antes de medir el nuevo diseño y no registres la misma navegación o rectángulo en las métricas de ruta y transición.
- Respeta
prefers-reduced-motion: reduce: conserva el cambio de estado y la entrada de historial, pero desactiva o acorta la animación.
Contenido
- Modelar el ciclo de vida
- Gestionar ready, finished y abort
- Contar una sola vez historial y mediciones
- Crear una alternativa para reducir movimiento
- Ejecutar una matriz acotada
- Límite de capacidad de BotBrowser
- Conclusión práctica
Modelar el ciclo de vida
La especificación CSS View Transitions describe una instantánea del renderizado antiguo, un callback de actualización y un renderizado nuevo que puede animarse mediante pseudo-elementos. La entrada JavaScript es document.startViewTransition(updateCallback). Devuelve inmediatamente un objeto ViewTransition, aunque el callback y las capturas ocurran después. El valor devuelto es un control del ciclo de vida, no una promesa de que la ruta ya terminó.
Haz explícitos los estados:
| Estado | Significado | Registro seguro |
|---|---|---|
requested | La aplicación aceptó una solicitud | navigationId, ruta, preferencia de movimiento |
updating | El callback cambia el DOM propio | updateStarted: true |
ready | Los pseudo-elementos nuevos están disponibles | ready: true, sin afirmar que terminó el pintado |
animating | Corre la animación o una ruta instantánea | animationMode |
finished | La transición se estabilizó o se omitió | finished: true |
aborted | La captura, actualización o animación no continúa | abortReason, estado alternativo |
Así no cuentas requested, ready y finished como tres navegaciones. Son fases de un evento. Conserva un navigationId estable y escribe un único resultado terminal. El navegador puede omitir la animación y aun confirmar el DOM; finished no implica una duración visible distinta de cero.
Detecta la función en el límite. Si falta document.startViewTransition, selecciona la misma actualización de ruta sin captura visual. No cambies las reglas de historial por tener o no la mejora.
Confirmar el estado del DOM una sola vez
El callback de actualización es la autoridad del nuevo DOM. Mantenlo síncrono y acotado. Obtén los datos antes de solicitar la transición o confirma primero un estado de carga. Un segundo clic debe ignorarse o recibir un navigationId deliberado; no debe crear dos entradas.
let navigationId = 0;
function showRoute(route, render) {
const id = ++navigationId;
const commit = () => {
render(route);
document.documentElement.dataset.route = route;
};
if (!document.startViewTransition) {
commit();
return Promise.resolve({ id, mode: 'fallback', outcome: 'committed' });
}
const transition = document.startViewTransition(commit);
return transition.finished.then(
() => ({ id, mode: 'view-transition', outcome: 'finished' }),
error => ({ id, mode: 'view-transition', outcome: 'aborted', error: error.name })
);
}
Renderiza desde un registro de ruta congelado y actualiza la tienda una sola vez. Un observador que muta otro elemento durante la captura puede mezclar etiquetas de dos rutas. Eso es una carrera de la aplicación, no una razón para reintentar a ciegas. Usa nombres view-transition-name solo para objetos semánticamente iguales; un nombre duplicado puede abortar la captura. El diseño sin nombres debe seguir siendo legible.
Gestionar ready, finished y abort
transition.ready se resuelve cuando el árbol de pseudo-elementos está listo para animar. Sirve para aplicar una clase, pero no significa que una persona haya visto la página. transition.finished se resuelve cuando acaba la animación o una ruta sin animación. Úsalo para limpiar clases y liberar el bloqueo de transición, no para emitir un segundo evento de ruta.
const transition = document.startViewTransition(updateRoute);
const record = { navigationId, ready: false, outcome: 'updating' };
transition.ready.then(
() => {
record.ready = true;
record.outcome = 'animating';
},
error => {
record.outcome = 'aborted';
record.abortReason = error.name;
}
);
transition.finished.then(
() => {
record.outcome = 'finished';
releaseRouteLock();
},
error => {
record.outcome = 'aborted';
record.abortReason = error.name;
releaseRouteLock();
}
);
Si el callback lanza una excepción, conserva el documento actual y muestra el error o reintento existente. Distingue domCommitted de animationOutcome. Una actualización confirmada no necesita revertirse solo porque falló su animación. Mantén razones acotadas como unsupported, duplicate-name, update-error, reduced-motion y superseded.
Cuando una ruta nueva sustituye a otra transición, el callback tardío de la anterior no puede escribir el resultado. La ruta nueva debe renderizar desde el estado vigente, no restaurar una instantánea obsoleta.
Contar una sola vez historial y mediciones
pushState() o Navigation API decide si una ruta crea una entrada; startViewTransition() solo aporta tratamiento visual. Ejecuta la operación de historial una vez y pasa la ruta resultante al callback. Un evento popstate debe conservar la semántica de back/forward elegida por el router, sin añadir otra entrada porque comenzó una animación.
La guía de Navigation API e historial explica quién posee cada entrada. Conserva su navigationId en el registro de transición en vez de inventar otro contador. Una ruta tiene un intento de transición y un resultado visual terminal. Sin API, usa la misma llamada de historial y mode: "fallback".
Mide la geometría después de ready solo si preguntas por el nuevo estado capturado; mide después de finished solo si preguntas por el estado asentado. No sumes ambos rectángulos en una métrica.
function measureOnce(phase, navigationId, element) {
const rect = element.getBoundingClientRect();
return {
navigationId,
phase,
rect: {
top: Math.round(rect.top),
left: Math.round(rect.left),
width: Math.round(rect.width),
height: Math.round(rect.height),
},
};
}
Los números dependen de desplazamiento, zoom, fuentes y ruta. Guarda viewport y offset de visualViewport cuando sean parte del criterio; no conviertas el rectángulo en identidad. Para back/forward, deja que el evento histórico elija la entrada. Marca una solicitud posterior como superseded antes de renderizarla.
Crear una alternativa para reducir movimiento
La referencia MDN de prefers-reduced-motion describe una preferencia del usuario. Respétala antes de elegir un desplazamiento largo. El estado, el foco, la ruta y el historial siguen ocurriendo; solo cambia la animación.
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.18s;
animation-timing-function: ease-out;
}
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-duration: 0s;
animation-iteration-count: 1;
}
}
Lee la preferencia actual en cada navegación. La rama JavaScript que omite la API debe ejecutar el mismo commit. Registra animationMode: "instant" y motionPreference: "reduce", sin guardar la preferencia como identidad del perfil.
Una transición no crea trampa de foco, nombre accesible ni anuncio para lector de pantalla. Mueve el foco solo según el contrato de ruta y prueba teclado y tecnología de asistencia por separado. Evita un destello: conserva el encabezado nuevo en el DOM antes de declarar lista la ruta.
Ejecutar una matriz acotada
Usa un escenario same-origin, datos estables y un perfil nuevo por fila.
| Fila | Condición | Resultado esperado | Qué demuestra |
|---|---|---|---|
| A | API disponible, movimiento normal | DOM, ready y finished una vez | Ciclo preferido |
| B | API ausente | Mismo DOM e historial, mode: fallback | Compatibilidad |
| C | Nombre duplicado | Recuperación usable y razón de aborto | Error de captura |
| D | prefers-reduced-motion: reduce | Misma ruta y foco, animación instantánea | Control del usuario |
| E | Entrada atrás/adelante | Un evento, ningún pushState extra | Propiedad del historial |
| F | Nueva solicitud pendiente | Registro anterior superseded | Protección contra callbacks obsoletos |
{
"fixtureId": "vt-nav-01",
"navigationId": "route-17",
"mode": "view-transition",
"motionPreference": "no-preference",
"domCommitted": true,
"ready": true,
"outcome": "finished",
"historyAction": "push",
"measurementPhase": "ready",
"browserVersion": "155.0"
}
Conserva una sola historyAction, un navigationId y como máximo una fase de medición. No copies consultas, texto de página ni transcripciones de foco. Si la fila E crea dos entradas, corrige el router antes del CSS; si D cambia el foco, es una regresión de accesibilidad aunque la imagen sea correcta.
Límite de capacidad de BotBrowser
BotBrowser puede cargar este escenario autorizado, conservar ruta y preferencia, y registrar etiquetas de ciclo visibles. No puede forzar startViewTransition, garantizar tiempos del compositor ni certificar anuncios de asistencia. La repetición describe ese navegador, viewport, perfil y hoja de estilos.
Fuentes
- CSS View Transitions Module Level 1 (CSSWG)
- MDN:
Document.startViewTransition() - Chrome for Developers: View Transitions
- MDN:
prefers-reduced-motion - Capacidades de plataforma de BotBrowser
Conclusión práctica
Asigna una decisión de ruta, un commit del DOM y un resultado terminal de transición. Usa ready para la fase capturada, finished para limpiar y un aborto explícito para rutas incompatibles o fallidas. Conserva historial y foco cuando se reduce o elimina el movimiento, y mide geometría en una única fase nombrada. BotBrowser puede repetir la matriz acotada; la aplicación y una revisión de accesibilidad siguen siendo responsables de la experiencia.
Este artículo forma parte de la guía: Superficies de la plataforma web: mapa de revisión
Artículos Relacionados
Navigation API, historial y límites de privacidad
PlataformaPreferencias de media queries y control del usuario
PlataformaRevisión de compatibilidad del navegador para Web Components
PlataformaCaché de atrás/adelante: ciclo de vida, privacidad y coherencia
PlataformaCompute Pressure API: cargas adaptativas y privacidad
PlataformaAislamiento de content scripts en extensiones del navegador
Lleva BotBrowser de la investigación a producción
Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.