Selectores y la cascada
En resumen: Un selector CSS elige los elementos a los que se aplica una regla, y la cascada decide qué regla gana en un conflicto: origen, especificidad y orden.
Problema
El precio no se pone rojo
La tienda empieza una oferta: el precio del pan debe ser rojo. Un compañero añade una regla para .price al final de style.css. El precio se hace más grande y en negrita, pero sigue gris oscuro.
Consola
La consola está vacía: CSS no da errores. La regla es la última del archivo y sí alcanza el precio, porque el tamaño y la negrita llegan. Solo el color no. Otra regla quiere otro color para el mismo párrafo, y el navegador eligió esa.
La idea
Regla y selector
CSS dice cómo se ve cada elemento del árbol. Lo dice con reglas:
.price { color: #c0392b; font-size: 20px;}.pricees el selector: a qué elementos se aplica la regla.- Entre las llaves están las declaraciones: propiedad, dos puntos, valor y punto y coma.
Los selectores buscan en el árbol, así que son como preguntas que se le hacen:
p(tipo): todos los<p>..price(clase): todos los elementos conclass="price". Un elemento puede tener varias clases:class="price sale".#product(id): el elemento conid="product".[type="email"](atributo): los elementos con ese atributo y ese valor, por ejemploinput[type="email"].article p(descendiente, con espacio): un<p>en cualquier lugar dentro de un<article>, a cualquier profundidad.nav > a(hijo): un<a>que es hijo directo de<nav>..price.sale(sin espacio): un elemento con las dos clases.h1, h2(lista): la regla vale para ambos.
Los estilos se escriben sobre todo con clases. Un tipo alcanza a todos los párrafos del sitio, y un id es para un solo elemento. Una clase va justo donde la necesitas y no depende de cómo esté organizado el árbol.
Pruébalo
Alcanza justo estos
Quita el subrayado solo de los enlaces del menú (el enlace de abajo se queda subrayado) y pon los dos precios en negrita sin poner en negrita las descripciones. Las propiedades son text-decoration: none y font-weight: 700.
Consola
Solución
index.html<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>La tienda</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<a href="/bread">Pan</a>
<a href="/milk">Leche</a>
<a href="/cart">Carrito</a>
</nav>
</header>
<main>
<article>
<h2>Pan Dobrudzha</h2>
<p>Pan blanco, 650 g, horneado esta mañana.</p>
<p class="price">1,80 €</p>
</article>
<article>
<h2>Leche fresca 3%</h2>
<p>Un litro, de una granja cerca de Teteven.</p>
<p class="price">2,40 €</p>
</article>
</main>
<footer>
<a href="/terms">Condiciones de envío</a>
</footer>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
nav { display: flex; gap: 16px; }
nav a { text-decoration: none; }
.price { font-weight: 700; }
nav a alcanza solo los enlaces de <nav>, mientras que a alcanzaría también el enlace de abajo. .price alcanza los dos párrafos con la clase, mientras que p pondría en negrita también las descripciones. Sin el punto, price busca un elemento <price>, y no existe.
La idea
La cascada
Al precio del principio lo alcanzan dos reglas, y las dos fijan color. El navegador tiene que elegir una. Esta regla de elección se llama cascada. Compara en tres niveles y se detiene en el primero que decide:
- Origen. El navegador tiene estilos propios: por eso
<h1>es grande y un enlace es azul y subrayado. Tu CSS los gana. - Especificidad. Cuán preciso es el selector. Son tres números: cuántos ids, cuántas clases, cuántos tipos.
pes 0,0,1,.pricees 0,1,0,#product pes 1,0,1. Los atributos ([type="email"]) y los estados (:hover) cuentan como clases. Los números se comparan columna por columna desde la izquierda. En cuanto una columna difiere, está decidido: 1,0,1 gana a 0,1,0. - Orden. Cuando la especificidad es igual, gana la regla que va más tarde en el código.
La cascada decide cada propiedad por separado. Por eso font-size y font-weight de .price llegan: nada más compite por ellas. Por color sí compite algo, y es más fuerte.
Existe también !important: color: #c0392b !important gana a toda declaración normal, sea cual sea la especificidad. Funciona, pero después cada cambio del color necesita un nuevo !important. Así que es señal de que en algún sitio un selector es demasiado fuerte, no una solución.
Míralo
Mira cómo elige el navegador
Cuatro reglas y un elemento. Observa cómo el navegador decide color y font-size por separado.
/* <article id="product"> <p class="price sale">1,80 €</p> </article> */p { color: #555; font-size: 16px; }.price { color: #c0392b; font-size: 20px; }#product p { color: #222; }.sale { font-size: 24px; }Paso a paso, en palabras
- Un elemento: un <p> con las clases price y sale, dentro de <article id="product">. Los cuatro selectores lo alcanzan. Dos de las reglas quieren fijar color y tres quieren font-size.
- La especificidad de un selector son tres números: cuántos ids, cuántas clases, cuántos tipos. #product p tiene un id y un tipo: 1,0,1.
- color: lo fijan tres reglas. El navegador compara la primera columna: 1 gana a 0, y no mira más. Gana #product p, aunque .price viene después.
- font-size: otra vez tres reglas. La columna de id es igual (0). En la columna de clases, p queda fuera con 0. .price y .sale son iguales en las tres columnas.
- Con la misma especificidad gana la regla que está más tarde en el código. .sale está en la línea 7, después de .price, por eso font-size es 24px.
- Cada propiedad tiene su propio ganador: color de #product p, font-size de .sale. La regla .price alcanza el elemento y aun así pierde en las dos.
Aquí está el mismo código en el navegador. El precio es gris oscuro y de 24px, como en la escena. Prueba a intercambiar .price y .sale: el tamaño cambia y el color no. Abre las herramientas de desarrollo (F12), selecciona el precio y mira el panel Estilos: las declaraciones que pierden aparecen tachadas.
Consola
La idea
Herencia
Algunas propiedades pasan del padre a los hijos. Si un elemento no tiene una regla propia para ellas, toma el valor de su padre. Esto es la herencia.
- Las propiedades del texto se heredan:
color,font-family,font-size,font-weight,line-height,text-align. - Las de la caja no:
border,padding,margin,background,width. Un borde alrededor de la tarjeta no significa un borde alrededor de cada párrafo de dentro.
Un valor heredado es el más débil. Cualquier regla que alcance el elemento directamente lo gana, incluso p con 0,0,1. Los botones y los campos reciben del navegador su propia fuente y su propio color, por eso no heredan. inherit pide la herencia de forma explícita: button { font-family: inherit; }.
Consola
El texto de la tarjeta es verde y en Georgia, pero el borde está solo alrededor de la tarjeta. El botón tomó la fuente por inherit, y su color se quedó negro: no hay una regla así para color.
Pruébalo
Arregla el precio
Pon el precio en rojo (#c0392b) sin !important. La descripción debe seguir en gris oscuro (#222).
Consola
Solución
index.html<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Pan Dobrudzha</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<article id="product">
<h1>Pan Dobrudzha</h1>
<p>Pan blanco, 650 g, horneado esta mañana.</p>
<p class="price">1,80 €</p>
<button type="button">Añadir al carrito</button>
</article>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
#product { color: #222; line-height: 1.5; }
h1 { font-size: 24px; }
/* Oferta: el precio es rojo */
.price { color: #c0392b; font-size: 20px; font-weight: 700; }
No hace falta un selector más fuerte para el precio, sino uno más débil para la descripción. #product { color: #222; line-height: 1.5; } le da el color al artículo, y los párrafos lo heredan. El valor heredado pierde frente a .price, que alcanza el precio directamente. #product .price (1,1,0) también pasa, pero entonces el siguiente cambio necesitará un selector aún más fuerte. p.price no basta: 0,1,1 sigue perdiendo contra 1,0,1 en la primera columna.
Pruébalo
Encuentra el error
El precio tiene las clases price y sale, pero no es rojo. ¿Por qué?
Elige la línea con el error.
El espacio en .price .sale significa «un elemento con la clase sale dentro de un elemento con la clase price». Aquí las dos clases están en un mismo elemento, y para eso se escriben juntas: .price.sale.
Pruébalo
Completa
El menú tiene enlaces y una lista anidada con más enlaces. Solo los enlaces que son hijos directos de <nav> deben quedar sin subrayado.
nav a {
text-decoration: none;
}> alcanza solo a los hijos directos. Con un espacio (nav a) la regla alcanzaría también los enlaces de la lista anidada.
Más precisamente, para curiosos
Origen. Hay tres fuentes: el navegador, el usuario (ajustes y extensiones) y el autor, que eres tú. En las declaraciones normales el autor gana al usuario, y el usuario gana al navegador. Con !important el orden se invierte: el !important del usuario gana al tuyo. Así una persona con poca vista puede imponer una fuente mayor en cualquier sitio.
style="…". Un estilo en el atributo del elemento gana a cualquier selector del archivo. Así que también es una trampa, como !important.
Las columnas no se desbordan. 0,11,0 no es 1,1,0. Once clases no ganan a un id: la comparación termina en la primera columna.
Lo que no cuenta. * es 0,0,0. Los combinadores (espacio, >, +, ~) no suman nada. Los pseudoelementos (::before) cuentan como tipos.
:is(), :not() y :where(). :is() y :not() toman la especificidad de su argumento más fuerte. :where() es siempre 0,0,0: práctico para estilos por defecto que cualquier regla sobrescribe.
Capas. @layer ordena grupos enteros de reglas, y la capa decide antes que la especificidad. Las grandes bibliotecas de estilos lo usan para no pelearse con tu CSS.
Las palabras clave. inherit toma el valor del padre. initial es el valor inicial de la propiedad según el estándar (para display es inline). unset es inherit en las propiedades heredadas e initial en las demás. revert devuelve el estilo del navegador.
Ejercicio
La oferta en el catálogo
El catálogo tiene tres productos, y la leche está en oferta (la clase sale en su tarjeta). Todo el texto del catálogo es #222, y los precios van en negrita. El precio del producto en oferta es rojo (#c0392b), y el precio antiguo de dentro es gris (#777). La etiqueta −20% tiene texto blanco (#fff) sobre fondo rojo (#c0392b). Los demás precios se quedan en #222. No uses !important.
Consola
Solución
index.html<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Catálogo</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main id="catalog">
<article class="product">
<h2>Pan Dobrudzha</h2>
<p class="price">1,80 €</p>
</article>
<article class="product sale">
<h2>Leche fresca 3%</h2>
<p class="badge">−20%</p>
<p class="price"><s class="old">2,40 €</s> 1,92 €</p>
</article>
<article class="product">
<h2>Mantequilla 125 g</h2>
<p class="price">3,20 €</p>
</article>
</main>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
#catalog { color: #222; }
.price { font-weight: 700; }
.sale .price { color: #c0392b; }
.old { color: #777; }
.badge { color: #fff; background: #c0392b; }
#catalog p alcanza todos los párrafos con 1,0,1 y gana a toda regla hecha de clases. Por eso el color va en #catalog, y el texto lo hereda. Después .sale .price (0,2,0) alcanza solo el precio en oferta, .old le da el gris al precio antiguo (si no, heredaría el rojo del párrafo), y .badge le da a la etiqueta su color y su fondo. Los encabezados también son #222: heredan de #catalog.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: El documento HTML · Formularios
- Continúa en: El modelo de caja · Flexbox: fila y columna · Grid: filas y columnas · Diseño responsive
- Ejercicios sobre el tema: CSS: el aspecto de la página