Origen e importancia: los ocho escalones de arriba
Los tres orígenes de estilo, los dos pseudo-orígenes de animación y transición, y la lista completa de precedencia que decide antes que cualquier otra cosa.
El primer criterio de la cascada no compara selectores: compara procedencias. Y como es el primero, cuando dos declaraciones vienen de orígenes distintos la comparación termina ahí y todo lo demás sobra. La lista tiene ocho escalones, incluye dos procedencias que no son hojas de estilo —las animaciones y las transiciones— y su orden explica media docena de comportamientos que se suelen atribuir a la magia: por qué no puedes sobrescribir una animación en marcha, por qué !important dentro de unos fotogramas clave se ignora, y por qué el atributo width de una imagen pierde contra cualquier regla tuya.
- Reproducir de memoria los ocho escalones de origen e importancia.
- Explicar por qué las declaraciones de animación se sitúan entre las importantes y las normales de autor.
- Predecir el resultado de intentar sobrescribir una animación o una transición en curso.
- Situar las sugerencias de presentación del marcado en la lista de precedencia.
Tres orígenes, tres legitimidades
Toda declaración pertenece a uno de tres orígenes, y cada uno responde a un interés distinto.
El origen de agente de usuario es la hoja de estilos que el navegador aplica a todo documento. Es la que decide que un h1 sea grande, que una lista lleve marcadores, que un enlace se subraye y que un input tenga aspecto de campo. Existe para que un documento HTML sin ninguna hoja de autor siga siendo legible y operable.
El origen de usuario son los estilos que impone la persona que lee. Es hoy el menos visible de los tres: casi ningún navegador ofrece una interfaz cómoda para editarlo, y la mayoría de las extensiones que “cambian el aspecto de una web” inyectan en realidad estilos de autor. Pero el mecanismo sigue existiendo en la especificación y sigue estando por encima de tus estilos cuando ambos son importantes, que es lo que garantiza que una necesidad de accesibilidad pueda imponerse sobre el diseño de un sitio.
El origen de autor eres tú, y por extensión cualquier CSS de terceros que hayas decidido cargar. Nota bien esto último: el CSS de una biblioteca de componentes que instalas está en tu mismo origen. La cascada no distingue entre “mi CSS” y “el CSS del paquete que instalé”; ese es precisamente el problema que resuelven las capas.
La lista completa, de mayor a menor precedencia
| # | Escalón | Comentario |
|---|---|---|
| 1 | Declaraciones de transición | Nada puede sobrescribir una transición en curso |
| 2 | Importantes del agente de usuario | El navegador se reserva lo innegociable |
| 3 | Importantes del usuario | La accesibilidad gana al diseño |
| 4 | Importantes del autor | Tu !important |
| 5 | Declaraciones de animación | Por encima de tus reglas normales |
| 6 | Normales del autor | Tu CSS de todos los días |
| 7 | Normales del usuario | Preferencias sin insistencia |
| 8 | Normales del agente de usuario | La hoja por defecto del navegador |
La forma de leerla es notando que los orígenes aparecen dos veces, en orden inverso. Del 6 al 8 el orden normal va de autor a navegador; del 2 al 4 el orden importante va de navegador a autor. Esa simetría invertida es todo el significado de !important: no es “más peso”, es cambiar de mitad de la tabla, y en la mitad de arriba la jerarquía de autoridad se da la vuelta para que quien menos manda en condiciones normales sea quien más manda cuando insiste. La lección siguiente desarrolla las consecuencias de esa inversión.
Falta una fila que la especificación trata aparte y que conviene conocer: las sugerencias de presentación del lenguaje de marcado. Cosas como width="600" en una imagen, el viejo bgcolor de una tabla o los atributos de presentación de SVG entran en la cascada como un origen especial situado entre el usuario normal y el autor normal. La consecuencia práctica es la que ya intuías: cualquier regla tuya, por débil que sea, vence a un atributo de presentación del HTML.
/* Gana esta regla, aunque el HTML diga width="600" */
img { inline-size: 100%; block-size: auto; }
Es lo que hace que un reset de imágenes responsivas funcione sin tener que tocar el marcado que genera un gestor de contenidos.
Animaciones y transiciones: por qué están donde están
Los escalones 1 y 5 no son hojas de estilo: son valores que el motor genera mientras algo se mueve. Su posición en la lista no es arbitraria, y se deduce razonando qué pasaría si estuvieran en otro sitio.
Las animaciones están en el 5, justo encima de tus declaraciones normales. Tienen que estarlo, porque si estuvieran por debajo no podrían animar nada: cualquier regla normal que fijara la propiedad las anularía y el elemento se quedaría quieto. Y no pueden estar más arriba del 4, porque entonces no habría manera de que el autor impusiera un valor sobre una animación en ningún caso.
De ahí salen dos comportamientos que desconciertan y que ahora son deducibles.
El primero: mientras una animación está aplicada a una propiedad, una declaración normal tuya no la sobrescribe, por muy específico que sea el selector. La única forma de ganarle desde CSS es !important, que está en el escalón 4.
El segundo, complementario y menos conocido: !important dentro de un bloque de fotogramas clave se ignora. La especificación de animaciones lo dice explícitamente. Tiene sentido: los fotogramas describen valores para el escalón 5, y permitirles marcar importancia sería dejarles saltar a un escalón que no les corresponde.
@keyframes latido {
from { transform: scale(1); }
50% { transform: scale(1.08) !important; } /* el !important se ignora */
to { transform: scale(1); }
}
Las transiciones están en el 1, por encima de absolutamente todo. También es deducible. Una transición no propone un valor nuevo: interpola hacia un valor que ya ganó la cascada. Si algo pudiera sobrescribir el valor intermedio de una transición, el elemento saltaría bruscamente al destino en lugar de llegar suave, que es justo lo contrario de lo que se pidió. Poner la transición en lo más alto es la manera de garantizar que, una vez decidido el destino, el camino no se interrumpe.
El caso real donde todo esto se junta es un botón con una animación de entrada que además tiene que reaccionar al puntero. Escribes una animación que anima transform, y luego una regla de :hover que también toca transform, y el :hover no hace nada. No es un bug de especificidad: la animación está en el escalón 5 y tu regla normal en el 6, así que la comparación termina antes de mirar el selector. Lo que casi todo el mundo hace a continuación es poner !important en el :hover, y funciona —el escalón 4 gana al 5—, pero deja una declaración importante en el código que envenenará cualquier sobrescritura futura. Hay tres salidas mejores, y elegir entre ellas es una decisión de diseño real. La primera es repartir el trabajo entre propiedades distintas: anima con las propiedades individuales translate, scale y rotate, que son propiedades separadas y componen con transform en lugar de pisarlo, de modo que la animación toque scale y el :hover toque translate y no compitan nunca. La segunda es dejar de competir con la animación y modificarla desde dentro, animando hacia una custom property que el :hover cambia; las variables se leen en el momento de calcular el valor y esquivan por completo el conflicto de escalones. La tercera es retirar la animación cuando ya no la necesitas, con animation-fill-mode y un evento de fin, para que la propiedad vuelva al escalón 6 y las reglas normales recuperen el control. La primera es la que menos código deja y la que más gente desconoce, porque translate, scale y rotate como propiedades independientes son relativamente recientes y casi nadie las ha incorporado a su vocabulario.
La hoja del navegador no es basura que borrar
El escalón 8 tiene mala fama por una razón histórica caducada. Cuando las diferencias entre motores eran grandes, un reset agresivo que ponía todo a cero era la única forma de partir de un terreno conocido, y quedó como reflejo cultural. Hoy las diferencias son pequeñas y el reflejo cuesta más de lo que ahorra.
Lo que aporta esa hoja no es solo apariencia, es semántica visible. Un h1 grande comunica jerarquía a quien mira. Un enlace subrayado comunica que es pulsable a quien no distingue colores. Un fieldset con su borde comunica agrupación. Un input con aspecto nativo comunica que se puede escribir en él. Cuando lo borras todo, tienes que reconstruir esa información, y reconstruirla peor es lo normal.
El enfoque moderno tiene dos partes. La primera es que el reset se reduce a decisiones de proyecto, no a igualar navegadores: el modelo de caja, el comportamiento de las imágenes, la herencia de tipografía en los controles de formulario, la eliminación de los márgenes por defecto que estorban a un sistema de espaciado.
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
img, picture, video, canvas, svg {
display: block;
max-inline-size: 100%;
}
input, button, textarea, select { font: inherit; }
Ocho líneas, ninguna de las cuales existe para compensar una diferencia entre motores: todas son decisiones.
La segunda parte es dónde va ese reset, y ahí está la conexión con lo que viene. Un reset colocado en una capa temprana pierde por construcción contra todo lo demás, sin necesidad de que sus selectores sean débiles ni de que tu CSS sea fuerte. Eso es el nivel 4 de esta guía. Antes hay que resolver el otro criterio del primer escalón, que es !important y su inversión.