Si eres desarrollador/a de CSS estarás muy habituada/o a utilizar con maestría las pseudo-clases. Pero ¿conoces todas las que hay? ¿las usas todas?, ¿les sacas todo el potencial que pueden darte?

Sabemos que las pseudo-clases no son los selectores CSS con mejor performance, más bien al contrario, pero nos permiten manejar y mantener mejor nuestro código y es aconsejable tener consenso en equipos grandes sobre su utilización e impacto en el desarrollo de la arquitectura CSS de nuestros proyectos.

Pseudo-clases vs pseudo-elementos

Para quienes no estéis familiarizados/as o estéis empezando a desarrollar con CSS es probable que no tengáis muy claro qué diferencia hay entre las pseudo-clases y los pseudo-elementos:

Y lo más importante: su sintaxis es distinta. Esto es muy importante ya que en la antigua especificación de CSS los pseudo-elementos se escribían con la misma sintaxis que las pseudo-clases y eso ha generado “malos hábitos” en algunos/as desarrolladores/as que siguen escribiéndolos “a la vieja usanza” (cosa fácilmente evitable si utilizamos un linter para nuestro CSS,) por eso es importante que recordemos que:

// Los pseudo-elementos se escriben con doble dos puntos
.mi-clase::after {}
.mi-clase::before {}

// Las pseudo-clases se escriben con sólo un doble punto
.mi-clase:hover {}
.mi-clase:not {}

Pseudo-clases

Muchas de ellas las utilizamos en nuestro día a día de manera automática y por eso solemos creer que controlamos completamente este tema. Pero hay algunas pseudo-clases más “desconocidas” que utilizamos poco (o directamente no utilizamos) que nos podrían evitar muchos quebraderos de cabeza. No os miento si os digo que el día que descubrí la pseudo-clase :not cambió mi vida.

Las vamos a dividir en subgrupos en función de su relación con los elementos del DOM y las interacciones de usuario.

  1. Basadas con la interacción del usuario con el elemento al que aplican.

Estas son probablemente las más conocidas y usadas, incluso por aquellos/as que no estáis muy familiarizados con CSS:

  1. Para elementos de tipo link que hay presentes en el DOM: :any-link, :link, :visited, :local-link, :target, :target-within, :scope
  2. Para elementos que tienen un estado o lugar en la línea temporal su ejecución. Puede que esto os suene raro, pero se aplican a elementos multimedia: :paused, :playing, :current, :past, :future
  3. Relacionadas con el idioma del desarrollo:
  1. Basadas en la estructura del DOM.

Estas pseudo-clases también suelen ser muy utilizadas y el listado es largo. Hacen referencia al lugar que ocupa el elemento sobre el que queremos aplicar el estilo en el DOM.

< !-- Para el selector .parent p las pseudo-clases que debemos usar para cada elemento son: -->
<div class="parent">
  Child 1 // :first-child
  Child 2 // :nth-child(2)
  Child 3 // :nth-child(3)
  Child 4 // :nth-child(4)
  Child 5 // :last-child
</div>

nth-child() además de admitir el valor de la posición del elemento que queremos estilar, podemos utilizar even/odd para elegir los pares/impares o expresiones matemáticas como (2n +3) para selecciones más complejas. Podéis verlo más claro en el siguiente ejemplo:

See the Pen MWmNgNv by @NoemiMS on CodePen.

Si no dais con la fórmula que necesitáis para vuestra selección, podéis consultar este post en el que hay multitud de ejemplos aritméticos.

Por último, :nth-last-child() tiene el mismo comportamiento que nth-child pero empezando a contar desde el último elemento.

<div class="parent">
  Child 1 
  Child 2 // :nth-last-child(3)
  Child 3 // :nth-last-child(2)
  Child 4 // :nth-last-child(1)
</div>
<div class="parent">
  <div>Child</div>  // .parent div:first-of-type
  Child  
  <div>Child</div> // .parent div:last-of-type
  Child
 <h3> Child </h3>
</div>

:nth-of-type() y :nth-last-of-type() funcionan igual que :nth-child pero además de poder elegir la posición que ocupa el elemento podemos discriminar por el tipo de elemento. :nth-last-of-type() , es lo mismo pero empezando a contar desde el último elemento que coincida.

<div class="parent">
  <div>Child</div>  // .parent div:nth-of-type(1)
  Child  
  <div>Child</div>  // .parent div.nth-of-type(2)
  Child
 <h3> Child </h3>
<div>Child</div>  // .parent div:nth-last-of-type(2)
  Child  
  <div>Child</div> // .parent div:nth-last-of-type(1)
</div>

Para las pseudo-clases que admiten expresiones matemáticas, este tester también nos viene muy bien para poder hacer pruebas y encontrar el selector que más nos convenga.

  1. Para elementos de formulario

Son pseudo-clases que hacen referencia a estados de los distintos elementos que podemos encontrar en un formulario.

  1. Basadas en las relaciones entre elementos o selectores

Estas pseudo-clases están basadas en relaciones “lógicas” entre los elementos a los que aplican.

.parent :not( p, .title) {
   color: var(--c-primary);
}

Si aplicaramos estos estilos al siguiente código html, sólo los h3 tendrían como color de texto el color primario:

See the Pen qBXaWmz by @NoemiMS on CodePen.

See the Pen eYWqjrK by @NoemiMS on CodePen.

div:has(h3){
  background: purple;
}

// Con este código un div tendrá fondo naranja  solo si tiene un h3 dentro

div:has( + div){
  background: purple;
}

// Con este código sólo tendrían un fondo púrpura los divs que tuvieran inmediatamente dentro de ellos otro div

Conclusión

A pesar de que comentábamos al principio del post que no son los selectores CSS con mejor performance, no debemos olvidarnos de ellas porque son herramientas que nos facilitan mucho la vida cuando con un simple :not() o un .nth-child() nos ahorramos tener que llenar nuestro HTML de clases.

Además, como hemos visto también, estas pseudo-clases siguen evolucionando y se siguen proponiendo nuevas como en el caso de :has() por lo que espero que después de leer este post podáis usarlas algo más.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.