En su día promedio de escritura de CSS, es probable que ni siquiera piense en la precedencia en CSS. No surge mucho. ¡Pero sí importa! Aparece cada vez que varios selectores de CSS coinciden con un elemento con exactamente la misma especificidad.
Suponiendo que la especificidad es exactamente la misma, el orden sí importa.
Styles declaró ganar más tarde.
Dentro de una sola hoja de estilo
Digamos que tenemos algo de HTML como este:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://css-tricks.com/precedence-css-order-css-matters/styles.css">
</head>
<body>
<div class="module module-foo module-bar">
Module
</div>
</body>
</html>
El orden de los atributos en el HTML no importa. Es el orden en la hoja de estilo. Centrémonos en el background
:
/*
All of these selectors match
and they all have the same specificity
*/
.module {
background: #ccc;
padding: 20px;
max-width: 400px;
margin: 20px auto;
}
.module-foo {
background: orange;
}
/* LAST declared, so these property/values win */
.module-bar {
background: lightblue; /* I win! */
/* I still have all the _other_ styles as well */
}
Un ejemplo intencionalmente complicado
El pedido no se limita a una sola hoja de estilo. El orden de la hoja de estilo en el documento es aún más importante.
Consulte este documento con tres estilos distintos … eh … llamémoslos fragmentos. Un trozo es un <link rel="stylesheet">
, a <style>
bloque, o un @import
hoja de estilo ed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- #1 -->
<link rel="stylesheet" href="https://css-tricks.com/precedence-css-order-css-matters/1.css">
<!-- #2 -->
<style>
.module-baz {
background-color: pink;
}
</style>
</head>
<body>
<div class="module module-foo module-bar module-baz">
Module
</div>
<!-- #3 -->
<style>
@import "2.css";
/*
Contains
.module-bar { background: #f06d06; }
*/
</style>
</body>
</html>
Etiqueté los trozos n. ° 1, n. ° 2 y n. ° 3. Todos ellos contienen selectores CSS con la misma especificidad exacta. # 3 es el último declarado, por lo que gana la batalla de precedencia.
El CSS cargado de forma asíncrona aún respeta el orden de los documentos
Quizás esté cargando CSS con un cargador de CSS impresionante como loadCSS. ¿Qué sucede si tuviéramos que cargar un cuarto archivo CSS con él exactamente con la misma configuración que en el ejemplo “complicado” anterior?
loadCSS inyecta la hoja de estilo en la parte inferior del <head>
por defecto, por lo que se convertiría en el n. ° 3 y el <style>
el bloque en la parte inferior del cuerpo se convertiría en el número 4 y, por lo tanto, ganaría.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://css-tricks.com/precedence-css-order-css-matters/1.css">
<script src="loadCSS.js"></script>
<script>
loadCSS("2.css");
</script>
<!-- 2.css will be injected right here -->
</head>
<body>
<div class="module module-foo module-bar module-late">
Module
</div>
</body>
</html>
En realidad, no es válido (aunque funciona) tener un <link>
o <style>
bloquear como un hijo de la <body>
, por lo que sería muy raro que una hoja de estilo cargada por loadCSS no fuera la ganadora por defecto.
Además, puede especificar un ID al que apuntar para poder controlar el orden de CSS:
<script id="loadcss">
// load a CSS file just before the script element containing this code
loadCSS("path/to/mystylesheet.css", document.getElementById("loadcss"));
</script>
¿Critical CSS se vuelve extraño?
Una de las razones por las que podría usar loadCSS es porque intencionalmente está tratando de diferir la carga de su hoja de estilo, porque está inyectando CSS crítico en el <head>
para intentar incluir estilos en el primer paquete y acelerar el renderizado.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
/* #1
Critical CSS chunk up here */
</style>
<script>
/* #2
Load the rest of the CSS
*/
</script>
</head>
<body>
<div class="module module-foo module-bar">
Module
</div>
</body>
</html>
La práctica de CSS crítico implica subir los selectores de CSS a una parte superior. El trozo # 1. El fragmento de orden más bajo y más fácil de anular. Entonces, teóricamente, sí, podría haber conflictos / cambios en qué CSS se aplica cuando se compara la página con solo el CSS crítico aplicado y con el CSS completamente cargado. Pero la hoja de estilo se carga por completo y viene después del CSS crítico, por lo que, en última instancia, será según lo previsto.
Es posible que deba ajustar exactamente qué lo convierte en CSS crítico y qué no, para evitar cambios de estilo extraños.
¿Las extensiones se vuelven raras?
En un preprocesador, pueden.
Digamos que quieres darle estilo a una cosa con una variación:
<div class="module module-variation">Module</div>
Y el código del preprocesador (súper simplificado para propósitos de demostración) termina como:
%variation {
background: orange;
}
.module {
background: #ccc;
padding: 20px;
max-width: 400px;
margin: 20px auto;
}
.module-variation {
@extend %variation;
}
Pensarías … está bien, .module-variation
es el ÚLTIMO selector declarado, por lo que gana, por lo que el fondo debe ser naranja. Pero no lo es, porque la extensión mueve el selector a donde está definida la cosa que está extendiendo, que en nuestro caso es antes de lo que estamos tratando de anular. El CSS compilado es:
.module-variation {
background: orange;
}
.module {
background: #ccc;
padding: 20px;
max-width: 400px;
margin: 20px auto;
}
Entonces el background
es en realidad #ccc
, no lo que queremos. Probablemente sea más fácil resolver esto con especificidad en lugar de orden de origen.
Las extensiones nativas, en caso de que se conviertan en algo, presumiblemente serían menos confusas.
Es una tontería manejar
Nadie quiere pensar en esto. Ganar estilo con especificidad es mucho más fácil. Pero saberlo es una buena idea, porque la hinchazón de especificidad innecesaria también es un fastidio.
Y seguimos bailando.