Depurar la cascada con el navegador delante
Cómo leer el panel de estilos como lo que es —una lista ordenada por precedencia—, las tres causas distintas de un tachado y el método que resuelve cualquier conflicto en cuatro preguntas.
La teoría de la cascada solo sirve si se convierte en gestos concretos delante de un navegador abierto. Y ahí hay una barrera que casi nadie salva: el panel de estilos muestra el resultado del algoritmo, pero no explica qué criterio lo decidió, y usa la misma señal visual —el tachado— para tres situaciones que no tienen nada que ver. Esta lección cierra el nivel convirtiendo los seis criterios en un procedimiento de diagnóstico.
- Leer el panel de estilos entendiendo qué significa la posición de cada regla.
- Distinguir las tres causas posibles de una declaración tachada.
- Comprobar por separado si un selector casa y si una hoja se ha cargado.
- Aplicar un procedimiento fijo de cuatro preguntas ante cualquier conflicto.
El panel de estilos es una lista ordenada
Lo primero que hay que interiorizar es que las reglas del panel de estilos no aparecen en el orden en que están escritas en tus ficheros: aparecen ordenadas por precedencia, la ganadora arriba. Esa ordenación es literalmente la salida del algoritmo de la cascada aplicada a ese elemento, así que el panel es el algoritmo hecho visible.
Debajo del bloque de reglas que casan directamente vienen las secciones de heredado de, una por cada ancestro que aporta alguna propiedad heredable. Esa separación es la frontera entre cascada y herencia de la que hablamos en la primera lección del nivel, dibujada en la interfaz: lo de arriba compitió, lo de abajo se limitó a bajar por el árbol.
El panel de estilos calculados responde a otra pregunta y conviene no confundirlas. El de estilos dice quién compitió y quién ganó; el de calculados dice con qué valor se quedó el elemento, ya resuelto. Cuando la duda es “¿por qué gana esta regla?”, el panel correcto es el primero. Cuando la duda es “¿de dónde sale este valor?”, el segundo, que además permite desplegar cada propiedad para ver la regla de origen.
Un tercer sitio que casi nadie mira y que en CSS moderno se ha vuelto imprescindible: los inspectores actuales indican a qué capa en cascada pertenece cada regla. En un proyecto con capas, esa información resuelve por sí sola la mayoría de los “no entiendo por qué gana esta”: si dos reglas están en capas distintas, la comparación terminó en el cuarto criterio y contar especificidad es tiempo perdido.
Tres razones distintas para un mismo tachado
Aquí está el matiz que separa a quien depura rápido de quien da vueltas. Una declaración aparece tachada por tres motivos incompatibles entre sí:
Perdió la cascada. Hay otra declaración de la misma propiedad con más precedencia. Es el caso mayoritario y el que todo el mundo asume.
El valor es inválido. El motor descartó la declaración al analizarla. Los inspectores suelen añadir un icono de advertencia, pero es pequeño y se pasa por alto con facilidad. Una unidad mal escrita, una coma de más, una función que no existe.
La propiedad no aplica a ese elemento. La declaración es válida y gana, pero su definición dice que solo tiene efecto sobre otro tipo de caja. gap en un contenedor que no es flex ni grid, vertical-align en un bloque, z-index sin posicionamiento.
Las tres se distinguen en segundos si sabes qué mirar. Si hay otra declaración de la misma propiedad más arriba en el panel, es la primera. Si no la hay y aparece un aviso, es la segunda. Si no la hay y no hay aviso, es la tercera, y la respuesta está en la línea “se aplica a” de la documentación de la propiedad.
El caso realmente desconcertante no es el tachado: es cuando la declaración no aparece por ningún lado. Ahí la gente suele concluir “no se está aplicando” y se pone a tocar el selector a ciegas, cuando en realidad esa ausencia tiene cuatro causas distinguibles con dos líneas de consola. Puede que el selector no case con el elemento, y eso lo compruebas con el.matches('.mi-selector'), que devuelve un booleano sin ambigüedad. Puede que el selector case con otros elementos pero no con el que tienes seleccionado, y lo ves con document.querySelectorAll('.mi-selector').length comparado con el elemento concreto. Puede que la hoja no se haya cargado, y eso no se ve en el panel de estilos sino en el de red, buscando el fichero y su código de respuesta; una hoja que devuelve un 404 no produce ningún error visible en la página. O puede que la hoja se haya cargado pero la regla se descartara entera al analizarla, cosa que ocurre cuando el selector contiene algo que el motor no reconoce: en una lista de selectores separada por comas, un solo selector inválido invalida la regla completa, incluidos los selectores válidos que la acompañan. Esa última es la que produce escenas de incredulidad, porque el CSS se ve perfecto en el editor y la regla sencillamente no existe en el navegador. Es, dicho sea de paso, el argumento técnico más fuerte a favor de :is() y :where(), cuyas listas son tolerantes y descartan solo el selector defectuoso en lugar de llevarse la regla por delante. El hábito que hay que adquirir es este: ante una ausencia, no toques el CSS hasta haber comprobado con la consola si el selector casa y con la red si el fichero llegó. Son diez segundos y eliminan dos de las cuatro hipótesis.
Comprobaciones desde la consola
Tres líneas cubren casi todo. La primera confirma si un selector casa realmente con el elemento que tienes seleccionado en el inspector, al que se accede con $0:
$0.matches('.tarjeta:hover .titulo')
La segunda te da el valor final de una propiedad, ya resuelto, sin depender de cómo lo pinte el panel:
getComputedStyle($0).getPropertyValue('color')
Y funciona también con custom properties, cosa que se olvida y es enormemente útil cuando una variable se hereda desde un ancestro lejano:
getComputedStyle($0).getPropertyValue('--color-acento')
La tercera comprueba de un vistazo si una hoja se cargó y cuántas reglas trae, lo que basta para descartar un fichero que llegó vacío o que no llegó:
[...document.styleSheets].map(h => [h.href, h.cssRules.length])
Ten en cuenta que leer cssRules de una hoja servida desde otro dominio lanza una excepción de seguridad; que salte esa excepción ya te confirma que la hoja existe, así que la información sigue siendo útil.
El procedimiento en cuatro preguntas
Ante cualquier conflicto, en este orden y sin saltarse ninguno.
Uno. ¿Compiten de verdad? Comprueba que las dos declaraciones son de la misma propiedad y afectan al mismo elemento. Una cantidad sorprendente de conflictos aparentes son en realidad dos propiedades distintas —background frente a background-color, o una abreviada que expande sobre otra— o dos elementos distintos que se parecen en el marcado.
Dos. ¿Vienen del mismo origen y la misma importancia? Si una lleva !important y la otra no, ya has terminado. Si una la aporta una animación en curso, también.
Tres. ¿Están en la misma capa? Si no, la capa decide y la especificidad no se mira. En el inspector, la etiqueta de capa de cada regla lo dice.
Cuatro. Solo ahora, ¿qué especificidad tiene cada una? Y si empatan, gana la que aparezca después en el orden del documento, lo que incluye el orden de carga de los ficheros.
La virtud del procedimiento no es que sea sofisticado, sino que es el mismo orden que usa el motor. Cualquier atajo que empiece por contar puntos de especificidad está apostando a que los tres criterios anteriores empataron, y esa apuesta falla justamente en los casos difíciles, que son los únicos en los que estás depurando.
- Crea una página con dos reglas que compitan y haz que gane cada una de las dos alternando solo el orden de los ficheros, sin tocar los selectores.
- Repite el ejercicio metiéndolas en dos capas y comprueba que ahora el orden de los ficheros ya no cambia nada.
- Marca las dos como importantes y comprueba que el ganador se invierte respecto al paso anterior.
- Añade un
styleen línea al elemento y verifica que gana aunque el selector de la hoja tenga una especificidad enorme.