Saltar al contenido

CSS, de a poco

Curso de CSS

Pseudo-clases y pseudo-elementos

Lección 08 de 37 · 3 min

Una clase marca un elemento desde el HTML. Las pseudo-clases lo eligen por su estado o su posición, y los pseudo-elementos apuntan a una parte que no existe en el HTML.

Pseudo-clases: estado y posición

Una pseudo-clase (:hover, :checked…) empieza con : y filtra al elemento según algo que el HTML no dice. Las de estado cambian con lo que hace la persona:

  1. Puntero:hover · :active:hover con el puntero encima, :active mientras se presiona.
  2. Foco:focus · :focus-visible:focus cada vez que el elemento tiene el foco; :focus-visible solo cuando el navegador decide mostrarlo, como al usar el teclado. Para los anillos de foco, usá :focus-visible.
  3. Formularios:checked · :disabled · :user-invalid:checked y :disabled en formularios, y :user-invalid para marcar un campo inválido recién después de que la persona lo tocó.

Las de estructura miran la posición entre hermanos. :first-child y :last-child eligen los extremos, y :nth-child(an+b) prueba n = 0, 1, 2…: 2n+1 elige 1, 3, 5 (igual que odd) y -n+3 elige los tres primeros. :not() excluye, :is() y :where() agrupan, y :has() elige al elemento por lo que contiene: el famoso selector de padre.

Los siete días de la semana con tres selectores. :nth-child(2n+1) elige el lunes, el miércoles, el viernes y el domingo (posiciones 1, 3, 5 y 7, con n de 0 a 3). :nth-child(-n+3) elige los tres primeros, con n de 2 a 0. :last-child elige solo el domingo.

Prendé y apagá las reglas, tildá los ingredientes que ya tenés y pasá el puntero o el Tab por la lista. Las marcas naranjas nombran el selector que está actuando en cada fila, y el CSS de abajo es el que está aplicado.

li:has(:checked) · li::marker4 de 5 reglas activas

Bowl de garbanzos y palta

20 minutos · 2 porciones

Ingredientes

1 de 5 en la mesada
ingredientes.css
.ingredientes li:nth-child(odd) {
background-color: color-mix(in oklab, currentColor 6%, transparent);
}
.ingredientes li:hover,
.ingredientes li:has(:focus-visible) {
background-color: color-mix(in oklab, currentColor 12%, transparent);
}
.ingredientes label:has(input:checked) {
text-decoration: line-through;
opacity: 0.6;
}
.ingredientes {
counter-reset: ingrediente;
}
.ingredientes li {
counter-increment: ingrediente;
}
.ingredientes li::before {
content: counter(ingrediente) ".";
margin-inline-end: 0.5rem;
}
ingredientes.html
<ul class="[counter-reset:ingrediente]">
<li class="odd:bg-current/6 hover:bg-current/12 has-focus-visible:bg-current/12 [counter-increment:ingrediente] before:me-2 before:content-[counter(ingrediente)'.']">
<label class="has-checked:line-through has-checked:opacity-60">
<input type="checkbox" /> Palta madura
</label>
</li>
<li class="odd:bg-current/6 hover:bg-current/12 has-focus-visible:bg-current/12 [counter-increment:ingrediente] before:me-2 before:content-[counter(ingrediente)'.']">
<label class="has-checked:line-through has-checked:opacity-60">
<input type="checkbox" checked /> Garbanzos cocidos
</label>
</li>
<li class="odd:bg-current/6 hover:bg-current/12 has-focus-visible:bg-current/12 [counter-increment:ingrediente] before:me-2 before:content-[counter(ingrediente)'.']">
<label class="has-checked:line-through has-checked:opacity-60">
<input type="checkbox" /> Tomates cherry
</label>
</li>
<li class="odd:bg-current/6 hover:bg-current/12 has-focus-visible:bg-current/12 [counter-increment:ingrediente] before:me-2 before:content-[counter(ingrediente)'.']">
<label class="has-checked:line-through has-checked:opacity-60">
<input type="checkbox" /> Repollo morado
</label>
</li>
<li class="odd:bg-current/6 hover:bg-current/12 has-focus-visible:bg-current/12 [counter-increment:ingrediente] before:me-2 before:content-[counter(ingrediente)'.']">
<label class="has-checked:line-through has-checked:opacity-60">
<input type="checkbox" /> Batata asada
</label>
</li>
</ul>

Pseudo-elementos

Un pseudo-elemento (::before, ::marker…) empieza con :: y estiliza una parte de la caja. ::before y ::after crean un hijo al principio y al final, pero solo si tienen content. No funcionan en elementos reemplazados como <img>.

El paso de una receta con cinco partes que no están en el HTML: la estrella antes de la etiqueta Destacado (::before), la primera línea subrayada (::first-line), las palabras seleccionadas (::selection), las viñetas de la lista (::marker) y el texto de ejemplo del campo (::placeholder).

Ese contenido no se puede seleccionar, pero los lectores de pantalla en general sí lo leen. No pongas ahí nada importante, y si es decorativo, dale un texto alternativo vacío. La primera línea es para navegadores que no entienden la barra:

destacado.css
.destacado::before {
content: "★";
content: "★" / "";
}
  1. Viñeta::markerLa viñeta o el número de un ítem de lista. Acepta pocas propiedades, como color y las de fuente.
  2. Campos y selección::placeholder · ::selectionEl texto de ejemplo de un campo, y el texto que la persona seleccionó.
  3. Primera línea y letra::first-line · ::first-letterLa primera línea y la primera letra de un bloque.

En CSS2 los pseudo-elementos también usaban :, así que :before todavía funciona. Los nuevos, como ::marker, solo aceptan ::.

Cuánto pesan en la especificidad

Una pseudo-clase suma como una clase, y un pseudo-elemento como un tipo. Hay tres excepciones: :where() siempre vale 0, y :is(), :not() y :has() valen lo que su argumento más específico.

La especificidad de siete selectores, en id, clase y tipo: a:hover 0,1,1; li:nth-child(2n+1) 0,1,1; .lista li::marker 0,1,2; p::first-line 0,0,2; :where(.menu) a 0,0,1, porque :where() no suma; :is(#principal, .card) p 1,0,1, porque pesa el id de adentro; li:has(> input:checked) 0,1,2.

Por eso :where() sirve para estilos base fáciles de pisar, y por eso un solo id dentro de :is() sube el peso de toda la lista.

En Tailwind

En Tailwind las pseudo-clases y los pseudo-elementos son variantes: un prefijo con dos puntos delante de la clase. hover:bg-current/12 aplica el fondo solo con :hover, y before: ya agrega content vacío por vos.

CSSTailwind
:hover · :focus-visiblehover: · focus-visible:
:disabled · :user-invaliddisabled: · user-invalid:
:first-child · :nth-child(odd)first: · odd:
:nth-child(2n+1)nth-[2n+1]:
:not(:first-child) · :has(:checked)not-first: · has-checked:
::before · ::markerbefore: · marker:
::placeholder · ::selectionplaceholder: · selection:
tareas.html
<ul class="list-inside list-disc marker:text-current/50">
<li class="odd:bg-current/6 hover:bg-current/12">
<label class="has-checked:line-through has-checked:opacity-60">
<input type="checkbox" /> Regar las plantas
</label>
</li>
</ul>
<input type="email" placeholder="Tu email" required
class="placeholder:text-neutral-500 user-invalid:border-red-500" />