Selectores y propiedades
En la lección anterior escribiste h1 { color: red; } y todos los h1 se pusieron rojos. Pero en un proyecto real no quieres que todos los h1 sean iguales. Quieres que el título del hero sea enorme y naranja, el del blog sea mediano y blanco, y el del footer sea pequeño y gris. Para eso necesitas selectores.
Los selectores son el cerebro de CSS. Dominándolos, controlas exactamente qué elementos reciben qué estilos.
Selectores básicos
Selector de elemento
Selecciona todas las etiquetas de ese tipo en la página:
p {
line-height: 1.7;
max-width: 65ch;
}
a {
color: #3b82f6;
text-decoration: none;
}
Selector de clase (.clase)
Selecciona los elementos que tienen esa clase. Es el selector más usado con diferencia:
<article class="card">
<h2 class="card-title">Khruangbin — A La Sala</h2>
<p class="card-price">32,90 €</p>
<span class="badge badge--nuevo">Nuevo</span>
</article>
.card {
background: #141414;
border-radius: 12px;
padding: 1.5rem;
}
.card-title {
font-size: 1.25rem;
color: #fff;
}
.card-price {
color: #ff6b35;
font-weight: 700;
font-size: 1.5rem;
}
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 999px;
font-size: 0.8rem;
}
.badge--nuevo {
background: #22c55e;
color: #000;
}
Un elemento puede tener varias clases: class="badge badge--nuevo". Esto permite combinar estilos base con variantes.
Selector de ID (#id)
Selecciona un elemento único por su atributo id:
#hero {
min-height: 80dvh;
display: grid;
place-items: center;
}
Los IDs tienen una especificidad muy alta, lo que los hace difíciles de sobrescribir. En la práctica, usa clases para estilos y reserva los IDs para anclas o JavaScript.
Selector universal (*)
Selecciona todos los elementos. Útil para resets:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
Selectores modernos
CSS ha evolucionado mucho. Estos selectores son estándar en 2026 y simplifican código que antes requería trucos:
:is() — agrupar sin repetir
/* Antes: repetir el contexto */
article h2,
article h3,
article h4 {
color: #fff;
}
/* Ahora: con :is() */
article :is(h2, h3, h4) {
color: #fff;
}
:where() — como :is() pero sin especificidad
/* Estilos base fáciles de sobrescribir */
:where(article, section, aside) {
padding: 1.5rem;
}
/* Cualquier clase puede sobrescribir esto sin problemas */
.hero-section {
padding: 4rem;
}
:where() tiene especificidad 0, así que es perfecto para estilos por defecto que quieres que sean fáciles de personalizar.
:has() — el selector "padre"
Este fue el más esperado durante años. Permite estilizar un elemento basándote en lo que contiene:
/* Card que tiene imagen: más alta */
.card:has(img) {
min-height: 300px;
}
/* Formulario que tiene un campo inválido: borde rojo */
form:has(:user-invalid) {
border-color: #ef4444;
}
/* Nav que tiene más de 5 enlaces: layout diferente */
nav:has(a:nth-child(6)) {
flex-wrap: wrap;
}
:has() es revolucionario porque por primera vez CSS puede "mirar hacia abajo" en el árbol de elementos para decidir los estilos.
:not() — excluir elementos
/* Todos los enlaces excepto los del nav */
a:not(nav a) {
text-decoration: underline;
}
/* Todos los inputs excepto los checkboxes */
input:not([type="checkbox"]) {
border: 1px solid #333;
padding: 0.5rem;
}
Combinadores
Los combinadores definen la relación entre selectores:
/* Descendiente: cualquier p dentro de article (a cualquier nivel) */
article p {
margin-block-end: 1rem;
}
/* Hijo directo: solo los li que son hijos directos de .menu */
.menu > li {
font-weight: 600;
}
/* Hermano adyacente: el p que viene justo después de un h2 */
h2 + p {
font-size: 1.1rem;
color: #999;
}
/* Hermanos generales: todos los p que vienen después de un h2 */
h2 ~ p {
margin-inline-start: 1rem;
}
Selectores de atributo
/* Enlaces que abren en nueva pestaña: añadir icono */
a[target="_blank"]::after {
content: " ↗";
}
/* Inputs de tipo email */
input[type="email"] {
width: 100%;
}
/* Enlaces que empiezan con https */
a[href^="https"] {
color: #22c55e;
}
/* Archivos que terminan en .pdf */
a[href$=".pdf"]::after {
content: " (PDF)";
}
Pseudoclases de estado
.btn {
background: #ff6b35;
color: #fff;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s;
}
/* Cuando el cursor está encima */
.btn:hover {
background: #e55a2b;
}
/* Cuando se está pulsando */
.btn:active {
transform: scale(0.98);
}
/* Cuando se navega con teclado */
.btn:focus-visible {
outline: 2px solid #ff6b35;
outline-offset: 2px;
}
/* Input validado correctamente por el usuario */
input:user-valid {
border-color: #22c55e;
}
/* Input con error (solo tras interacción del usuario) */
input:user-invalid {
border-color: #ef4444;
}
:user-validy:user-invalidson la versión moderna de:validy:invalid. La diferencia: solo se activan después de que el usuario interactúe con el campo, no cuando la página carga. Mucho mejor experiencia.
Pseudoelementos
Crean elementos virtuales que no existen en el HTML:
/* Añadir contenido decorativo antes de cada título */
.section-title::before {
content: "—";
margin-inline-end: 0.5rem;
color: #ff6b35;
}
/* Primera letra del artículo más grande */
article p:first-of-type::first-letter {
font-size: 3rem;
font-weight: 700;
color: #ff6b35;
float: inline-start;
margin-inline-end: 0.5rem;
}
/* Placeholder del input */
input::placeholder {
color: #666;
font-style: italic;
}
Especificidad: quién gana
Cuando dos reglas compiten por el mismo elemento, el navegador calcula la especificidad para decidir cuál aplica. Piénsalo como un sistema de puntos:
| Selector | Especificidad | Ejemplo |
|---|---|---|
| Elemento | 0-0-1 | p, h1, div |
| Clase / pseudoclase | 0-1-0 | .card, :hover |
| ID | 1-0-0 | #hero |
| Inline style | 1-0-0-0 | style="..." |
!important |
Gana a todo | color: red !important |
/* Especificidad: 0-0-1 (un elemento) */
p { color: gray; }
/* Especificidad: 0-1-0 (una clase) — GANA */
.intro { color: white; }
/* Especificidad: 0-1-1 (una clase + un elemento) */
article .intro { color: #ccc; }
/* Especificidad: 1-0-0 (un ID) — GANA a todo lo anterior */
#hero-text { color: #ff6b35; }
La regla práctica: usa solo clases. Todas las clases tienen la misma especificidad base, así que el orden en el archivo determina quién gana. Mucho más predecible.
!importantrompe la cascada. Úsalo solo como último recurso (y en la lección 10 veremos por qué).
Estiliza la página de la tienda de vinilos
Amplía el HTML de Vinyl Paradise de la lección anterior con esta estructura y dale estilo usando solo clases:
- Un
.headercon el título y un.navcon 3 enlaces - Una sección
.featuredcon 3 cards de discos (.card,.card-title,.card-price) - Cada card con un badge
.badgeque diga "Nuevo", "Oferta" o "Último" - Estado
:hoveren las cards que cambie el color del borde - Usa
:has()para que las cards con badge "Oferta" tengan un fondo diferente - Un enlace externo con
a[target="_blank"]::afterque añada el icono ↗
lightbulb Pistas
Para el :has() del badge, puedes poner una clase específica al badge de oferta: .badge--oferta, y luego usar .card:has(.badge--oferta) { background: #1a1000; }. No uses IDs para los estilos — solo clases.