Saltar al contenido
schedule 12 min CSS

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-valid y :user-invalid son la versión moderna de :valid y :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.

!important rompe la cascada. Úsalo solo como último recurso (y en la lección 10 veremos por qué).

code

Estiliza la página de la tienda de vinilos

Medio schedule 15 min

Amplía el HTML de Vinyl Paradise de la lección anterior con esta estructura y dale estilo usando solo clases:

  • Un .header con el título y un .nav con 3 enlaces
  • Una sección .featured con 3 cards de discos (.card, .card-title, .card-price)
  • Cada card con un badge .badge que diga "Nuevo", "Oferta" o "Último"
  • Estado :hover en 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"]::after que 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.

Newsletter

Recibe nuevos cursos, actualizaciones, artículos del blog y promociones en tu correo.