view-transition-class: estilar grupos enteros
Aplicar el mismo estilo a muchos grupos sin enumerar nombres, la sintaxis del selector, y por qué es la pieza que hace usable match-element.
Con nombres únicos obligatorios, personalizar veinte elementos significaba escribir veinte selectores idénticos salvo por el identificador. view-transition-class resuelve exactamente eso: separa la identidad de un elemento capturado —su nombre, que debe ser único— de su categoría —su clase, que se comparte—. Es una propiedad pequeña que cambia por completo cómo se organiza el CSS de una transición grande.
- Declarar
view-transition-classy seleccionar por ella. - Combinar nombre y clase en el mismo selector cuando haga falta.
- Estilar los grupos generados por
match-element, que no tienen nombre legible. - Organizar el CSS de una transición con muchos elementos.
La propiedad y su selector
.tarjeta {
view-transition-name: match-element;
view-transition-class: tarjeta;
}
::view-transition-group(.tarjeta) {
animation-duration: 260ms;
animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}
El selector de los cuatro pseudo-elementos con nombre acepta, además de un identificador o el comodín, una o varias clases precedidas de punto. La sintaxis se lee igual que un selector de clase normal y significa lo mismo: casa con todos los grupos cuyos elementos declararon esa clase.
La propiedad acepta varias clases separadas por espacios, igual que class en HTML:
.tarjeta.destacada {
view-transition-class: tarjeta lenta;
}
Y el selector permite encadenarlas para exigir todas:
::view-transition-group(.tarjeta.lenta) {
animation-duration: 500ms;
}
También se puede combinar con un nombre concreto, que es la forma de decir “todos los de esta categoría, y además este en particular”:
::view-transition-group(vt-hero.tarjeta) {
z-index: 2;
}
El orden es siempre el nombre primero y las clases después, igual que en un selector de elemento con clases.
Está disponible en los tres motores: Chromium desde la 125, Firefox desde la 144 y Safari desde la 18.2.
Por qué hace usable match-element
Aquí está el encaje que justifica la propiedad. match-element genera nombres únicos internos que no puedes escribir en un selector: no los conoces y no son estables. Sin view-transition-class, las únicas opciones para estilar esos grupos serían el comodín * —que afecta también a root y a todo lo demás— o renunciar a personalizarlos.
Con las dos propiedades juntas, el patrón queda limpio: match-element se encarga de la unicidad y la clase se encarga del estilo.
.rejilla > article {
view-transition-name: match-element;
view-transition-class: celda;
}
::view-transition-group(.celda) {
animation-duration: 280ms;
animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}
::view-transition-old(.celda),
::view-transition-new(.celda) {
animation-duration: 160ms;
}
Cuatro reglas cubren una rejilla con cualquier número de elementos, sin generar nombres, sin índices en el marcado y sin JavaScript. Es la diferencia entre “esto es viable en producción” y “esto es una demo”.
La división de responsabilidades es la clave de por qué esta propiedad merece existir, y conviene enunciarla sin ambigüedad. view-transition-name empareja: es lo que hace que un elemento del estado antiguo y otro del nuevo se consideren el mismo, y por eso tiene que ser único. view-transition-class no empareja nada, no crea ningún grupo y no tiene que ser única: es exclusivamente un gancho de estilo sobre grupos que ya existen. De ahí salen dos consecuencias prácticas que no se ven de entrada. La primera: una clase sin nombre no hace nada. Poner view-transition-class a un elemento que no tiene view-transition-name es un no-operativo silencioso, porque ese elemento no genera ningún pseudo-elemento que estilar; forma parte de la captura de la raíz. Es un fallo que cuesta encontrar precisamente porque no produce ningún error, y es especialmente fácil de cometer al refactorizar, cuando alguien mueve el nombre a otro sitio y deja la clase huérfana. La segunda: como la clase se declara en el elemento y no en el CSS de la transición, el diseño de las categorías es una decisión de marcado, no de hoja de estilos. Esto es bueno —hace que añadir un elemento nuevo no requiera tocar el CSS— y tiene un coste que hay que asumir a conciencia: el conjunto de categorías vivas en un proyecto no se puede leer en ningún sitio, hay que buscarlo por el código. Documentar las tres o cuatro clases de transición que usa tu sistema, junto a los tokens de duración, vale más que cualquier comentario en el CSS.
Organizar el CSS de una transición grande
Con clases, el CSS de una transición compleja se puede estructurar en tres capas y deja de ser una lista plana de selectores.
Capa uno, los valores por defecto de la transición. Se aplican al comodín y establecen la personalidad del movimiento en todo el sitio.
::view-transition-group(*) {
animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}
Capa dos, las categorías. Una clase por tipo de elemento, con su ritmo propio.
::view-transition-group(.celda) { animation-duration: 260ms; }
::view-transition-group(.panel) { animation-duration: 340ms; }
::view-transition-group(.cabecera) { animation-duration: 200ms; }
Capa tres, las excepciones por nombre. Solo para los elementos que de verdad necesitan un tratamiento único.
::view-transition-group(vt-hero) {
animation-duration: 460ms;
z-index: 3;
}
Esa organización tiene la propiedad de que añadir un elemento nuevo al sitio no requiere tocar el CSS: basta con darle la clase que le corresponda. Y hace explícito que los nombres propios son la excepción, no la norma, que es justo la disciplina que evita que el CSS de transiciones crezca sin control.
Una advertencia sobre especificidad, porque muerde: los selectores de pseudo-elementos de transición siguen las reglas normales de especificidad, y ::view-transition-group(.celda) no gana automáticamente a ::view-transition-group(*). Si las tres capas están en el mismo fichero y en ese orden, el orden de aparición resuelve el desempate. Si están repartidas, conviene meterlas en capas de cascada explícitas para que el orden no dependa de en qué punto del bundle acabe cada fichero.