7. Capas
A veces dos reglas de CSS intentan dar estilo al mismo elemento y se contradicen. Una dice que un texto sea rojo y otra que sea azul. ¿Cuál gana? CSS tiene que elegir una, y no siempre resulta fácil adivinar cuál.
Las capas en cascada, o CSS Cascade Layers, nos ayudan a poner orden. Nos permiten agrupar los estilos en bloques con nombre, como reset o componentes, y decidir de forma clara qué bloque manda sobre cuál.
La idea es sencilla: tú eliges el orden de las capas y, cuando dos reglas chocan, gana siempre la que esté en la capa con más prioridad. Ni más ni menos. Vamos a verlo paso a paso.
Declarar el orden
Se usa la regla @layer. Lo primero, y más recomendable, es declarar el orden de las capas al principio de la hoja de estilos:
@layer reset, base, componentes, utilidades;
Con esa línea decidimos la prioridad de una vez: reset es la de menor peso y utilidades la de mayor. Cuanto más tarde se declara una capa, más manda.
Después vamos rellenando cada capa con sus estilos:
@layer reset {
* {
margin: 0;
padding: 0;
}
}
@layer base {
body {
font-family: sans-serif;
color: #333;
}
}
@layer componentes {
.tarjeta {
border: 1px solid #ccc;
padding: 1rem;
}
}
Fíjate en un detalle importante: el orden lo fija la primera declaración, no dónde escribas los estilos. Aunque el bloque @layer base esté antes en el archivo que @layer componentes, la prioridad ya quedó decidida en la línea inicial.
El orden manda sobre la especificidad
Veamos la regla de oro en acción. Tengo un párrafo al que le aplico color rojo mediante un id (muy específico) en la capa base, y color verde mediante una clase (poco específica) en la capa tema:
@layer base, tema;
@layer base {
#aviso {
color: red;
}
}
@layer tema {
.aviso {
color: green;
}
}
<p id="aviso" class="aviso">
Aunque el id es más específico, gano yo porque estoy en una capa posterior.
</p>
Aunque el id es más específico, gano yo porque estoy en una capa posterior.
El texto sale verde. La clase de la capa tema gana al id de la capa base simplemente porque tema se declaró después. La especificidad ha dejado de importar entre capas.
Añadir estilos a una capa existente
Una capa con nombre no se cierra. Puedes volver a ella tantas veces como quieras y los estilos se acumulan sin cambiar su prioridad:
@layer utilidades {
.margen-1 {
margin: 1rem;
}
}
/* Más adelante, incluso en otro archivo */
@layer utilidades {
.centrado {
text-align: center;
}
}
Esto es muy útil cuando reparto los estilos en varios archivos: cada uno aporta a la misma capa sin pisar a los demás.
Capas anidadas
Podemos crear capas dentro de capas usando un punto. Así una librería externa puede tener sus propias subcapas sin chocar con las nuestras:
@layer framework {
@layer layout {
main {
display: grid;
}
}
}
/* Equivale a apuntar directamente a la subcapa */
@layer framework.layout {
aside {
width: 20rem;
}
}
Importar dentro de una capa
Cuando traemos una hoja de estilos externa, podemos meterla entera en una capa con la función layer():
@import "normalize.css" layer(reset);
@import "bootstrap.css" layer(framework);
De esta forma, por muy agresiva que sea la especificidad de una librería, tus estilos la sobrescriben con solo colocarla en una capa anterior. Se acabó pelear contra el CSS de terceros.
Los estilos sin capa siempre ganan
Ojo con esto, porque despista al principio. Cualquier estilo que no esté dentro de una capa tiene más prioridad que todos los que sí lo están, sin importar cuántas capas tengas:
@layer base {
p {
color: green;
}
}
/* Este gana, porque vive fuera de cualquier capa */
p {
color: red;
}
El párrafo sale rojo. Lo puedes ver como que los estilos sin capa forman una capa invisible que está siempre la última, es decir, la más poderosa. Por eso conviene meter casi todo en capas y dejar fuera solo aquello que quieras que mande por encima de todo.
!important le da la vuelta a todo
Con !important, el orden de las capas se invierte. La primera capa declarada pasa a ser la más fuerte, y los estilos sin capa pasan a ser los más débiles:
@layer base, tema;
@layer base {
p {
color: green !important;
}
}
@layer tema {
p {
color: red !important;
}
}
Aquí el párrafo sale verde, aunque tema se declaró después. Con !important gana la capa anterior. Es una consecuencia lógica de la cascada, pero conviene tenerlo presente para no volverse loco depurando.
No conviertas las capas en una excusa para llenar el proyecto de !important. Su gracia es justamente evitarlos. Reserva !important para casos muy contados, como sobrescribir estilos que no controlas.
Volver a la capa anterior
Existe el valor revert-layer, que descarta lo que ha puesto la capa actual para una propiedad y deja que gane el valor de la capa anterior:
@layer base {
a {
color: blue;
}
}
@layer tema {
a {
color: revert-layer; /* Vuelve al azul de "base" */
}
}
Un caso práctico
Una arquitectura muy habitual reparte todo el proyecto en unas pocas capas ordenadas de menos a más prioridad:
/* 1. Fijamos el orden una sola vez */
@layer reset, base, componentes, utilidades;
/* 2. Rellenamos cada capa */
@layer reset {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
@layer base {
body {
font-family: sans-serif;
line-height: 1.5;
}
}
@layer componentes {
.boton {
padding: .5rem 1rem;
border-radius: .5rem;
background: royalblue;
color: white;
}
}
@layer utilidades {
.oculto {
display: none;
}
}
Con esta estructura ya no importa el orden en que escribas las reglas ni lo específicos que sean los selectores. Una utilidad como .oculto siempre ganará a un componente, y un componente siempre ganará a los estilos base. El CSS se vuelve predecible, que es exactamente lo que queremos.
Actividad 1
Crea dos capas, base y tema, declarando su orden en la primera línea. En base pinta los enlaces de negro usando un selector de id. En tema píntalos de rojo usando una clase. Comprueba que ganan los rojos aunque el id sea más específico.
Actividad 2
Descarga un normalizador como Normalize.css e impórtalo dentro de una capa reset con @import ... layer(reset). Añade después una capa componentes y verifica que tus estilos sobrescriben a los del normalizador sin necesidad de !important.
Este trabajo está bajo una licencia Attribution-NonCommercial-NoDerivatives 4.0 International.
Desafíos de programación atemporales y multiparadigmáticos
Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.
Comprar el libroAyúdame a seguir escribiendo
Cada café me da un empujón para escribir el siguiente artículo.
¡Claro, te invito!
Comentarios
Todavía no hay ningún comentario.