Pseudo-clases avanzadas
En el curso de Fundamentals aprendiste :hover, :focus, :first-child. Las pseudo-clases modernas van mucho más lejos: te permiten agrupar selectores, construir reglas potentes y escribir CSS más limpio y mantenible.
Pseudo-clases modernas: la caja de herramientas definitiva
Las pseudo-clases modernas de CSS permiten escribir reglas potentes, selectivas y limpias. Aquí tienes las más importantes, con ejemplos y notas de uso real:
:is() — agrupar selectores sin repetir
:is(header, main) :is(h1, h2, h3) {
color: var(--color-heading);
}
Especificidad: adopta la del selector más específico de la lista.
:where() — selector sin especificidad
:where(h1, h2, h3) { margin-top: 0; }
Especificidad: siempre 0. Útil para resets y utilidades.
:not() — excluir selectores
button:not(.primario) { opacity: 0.7; }
li:not(:last-child) { border-bottom: 1px solid var(--color-border); }
input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]) { border: 2px solid var(--color-border); }
a:not(:is(.btn, .nav-link, [aria-current])) { text-decoration: underline; }
Notas: acepta listas y puede anidar :is().
:has() — seleccionar el padre según sus hijos
.card:has(img) { display: grid; grid-template-columns: 200px 1fr; }
label:has(input:focus) { color: var(--color-primary); font-weight: 700; }
form:has(:invalid) .submit-btn { opacity: 0.5; pointer-events: none; }
article:not(:has(h2)) { padding-top: 2rem; }
Notas: permite selectores de padres, muy útil en UI avanzada (menús, accordions, tabs, tooltips…). Más ejemplos prácticos en la lección de CSS Ninja: “Menús, tooltips y UI con :has, :focus-within, etc.”
:nth-child() y familia — seleccionar por posición
li:nth-child(odd) { background: #f5f5f5; }
li:nth-child(3n) { font-weight: bold; }
li:nth-child(3 of .highlight) { background: yellow; }
p:nth-child(-n+3 of p) { font-size: 1.1em; }
Notas: la versión moderna acepta un selector extra con “of”.
:focus-visible — foco solo con teclado
button:focus:not(:focus-visible) { outline: none; }
button:focus-visible { outline: 3px solid var(--color-primary); outline-offset: 2px; }
Notas: mejora la accesibilidad y la experiencia de usuario.
:empty, :blank, :placeholder-shown
.notification-badge:empty { display: none; }
input:placeholder-shown + label { transform: translateY(0); font-size: 1rem; color: var(--color-text-muted); }
input:not(:placeholder-shown) + label { transform: translateY(-1.5rem); font-size: 0.75rem; color: var(--color-primary); }
Nota: Las pseudo-clases modernas se usan en componentes avanzados como menús, accordions y tabs. Consulta ejemplos prácticos en las lecciones de CSS Ninja: “Menús, tooltips y UI con :has, :focus-within, etc.” y “Accordions y tabs sin JS”.
Nota: Si buscas selectores de padres por sus hijos, como
:has, lo tienes en el curso CSS Ninja, bloque “De JS a CSS”.
:is(.card, p) { color: red; }
/* especificidad: 0,1,0 — porque .card tiene especificidad de clase */
:where() — cero especificidad
:where() funciona igual que :is() pero con especificidad cero. Útil para estilos base que quieres que cualquier otro CSS pueda sobreescribir fácilmente:
/* Estilos reset — sin especificidad para no competir con nada */
:where(h1, h2, h3, h4, h5, h6) {
font-weight: bold;
line-height: 1.2;
}
/* Este selector puede sobreescribirlo sin necesidad de más especificidad */
.article h2 {
line-height: 1.5;
}
La diferencia clave:
| Especificidad | |
|---|---|
:is(.card, p) | La del selector más específico de la lista |
:where(.card, p) | Siempre 0 |
:not() — excluir elementos
:not() excluye los elementos que coincidan con el selector interior. En la versión moderna acepta listas:
/* Sin el último elemento */
li:not(:last-child) {
border-bottom: 1px solid var(--color-border);
}
/* Todos los inputs excepto checkbox, radio y submit */
input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]) {
border: 2px solid var(--color-border);
padding: 0.5rem;
}
/* Lista compleja con :is() dentro de :not() */
a:not(:is(.btn, .nav-link, [aria-current])) {
text-decoration: underline;
}
:has() — seleccionar el padre
:has() es la pseudo-clase más esperada de los últimos años. Permite seleccionar un elemento en función de lo que contiene. Siempre se quiso “selector de padre” y aquí está:
/* .card que contiene una imagen */
.card:has(img) {
display: grid;
grid-template-columns: 200px 1fr;
}
/* label que contiene un input en foco */
label:has(input:focus) {
color: var(--color-primary);
font-weight: 700;
}
/* form que tiene campos inválidos */
form:has(:invalid) .submit-btn {
opacity: 0.5;
pointer-events: none;
}
/* artículo sin un h2 — estilo de reserva */
article:not(:has(h2)) {
padding-top: 2rem;
}
:has()es compatible con todos los navegadores modernos desde 2023. No lo uses en proyectos que requieran soporte de navegadores muy antiguos.
:nth-child() con selector
La versión moderna de :nth-child() acepta un selector como argumento adicional, algo que antes era imposible:
/* El tercer li que tenga clase .highlight */
li:nth-child(3 of .highlight) {
background: yellow;
}
/* Los primeros 3 elementos párrafo (ignorando otros tipos) */
p:nth-child(-n+3 of p) {
font-size: 1.1em;
}
Sin of, :nth-child(3) cuenta todos los hijos y selecciona el tercero si también coincide con el selector. Con of, solo cuenta los que coinciden con el tipo especificado.
:focus-visible — foco solo con teclado
:focus se activa con ratón y teclado. :focus-visible solo se activa cuando el foco es visible y relevante (navegación por teclado), no al hacer clic:
/* Quitar el outline molesto al hacer clic con ratón */
button:focus:not(:focus-visible) {
outline: none;
}
/* Pero mantenerlo para la navegación por teclado */
button:focus-visible {
outline: 3px solid var(--color-primary);
outline-offset: 2px;
}
:empty, :blank y :placeholder-shown
/* Ocultar un contenedor si no tiene contenido */
.notification-badge:empty {
display: none;
}
/* Etiquetar inputs mientras no tienen texto (efecto placeholder flotante) */
input:placeholder-shown + label {
transform: translateY(0);
font-size: 1rem;
color: var(--color-text-muted);
}
input:not(:placeholder-shown) + label {
transform: translateY(-1.5rem);
font-size: 0.75rem;
color: var(--color-primary);
}
En la siguiente lección trabajamos los pseudo-elementos: ::before, ::after, ::placeholder, ::selection y cómo usarlos para añadir decoración y contenido sin tocar el HTML.