Volver al Centro de conocimiento
Plataforma

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

Documentación

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.ready como el punto en que los pseudo-elementos capturados pueden animarse; transition.finished es 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

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:

Flujo desde la confirmación del DOM hacia ready, animación, finished, abort y alternativa de movimiento reducido

EstadoSignificadoRegistro seguro
requestedLa aplicación aceptó una solicitudnavigationId, ruta, preferencia de movimiento
updatingEl callback cambia el DOM propioupdateStarted: true
readyLos pseudo-elementos nuevos están disponiblesready: true, sin afirmar que terminó el pintado
animatingCorre la animación o una ruta instantáneaanimationMode
finishedLa transición se estabilizó o se omitiófinished: true
abortedLa captura, actualización o animación no continúaabortReason, 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.

FilaCondiciónResultado esperadoQué demuestra
AAPI disponible, movimiento normalDOM, ready y finished una vezCiclo preferido
BAPI ausenteMismo DOM e historial, mode: fallbackCompatibilidad
CNombre duplicadoRecuperación usable y razón de abortoError de captura
Dprefers-reduced-motion: reduceMisma ruta y foco, animación instantáneaControl del usuario
EEntrada atrás/adelanteUn evento, ningún pushState extraPropiedad del historial
FNueva solicitud pendienteRegistro anterior supersededProtecció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

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.

#View Transitions API#Navegación#Accesibilidad#Estado Del DOM#Plataforma Web

Este artículo forma parte de la guía: Superficies de la plataforma web: mapa de revisión

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.