Pseudo-clases en CSS

Las pseudo-clases son selectores especiales en CSS que nos permiten aplicar estilos a elementos según su estado o posición. Son fundamentales para crear interfaces interactivas y accesibles.

¿Qué es una pseudo-clase?

Una pseudo-clase se escribe con dos puntos : seguido del nombre de la pseudo-clase:

selector:pseudo-clase {
  /* estilos */
}

Las pseudo-clases más comunes relacionadas con la interacción del usuario son:

  • :hover - cuando el usuario pasa el ratón sobre el elemento
  • :focus - cuando el elemento tiene el foco (seleccionado)
  • :active - cuando el elemento está siendo activado/clicado
  • :disabled - cuando el elemento está deshabilitado

:hover - Al pasar el ratón

La pseudo-clase :hover se activa cuando el usuario pasa el cursor del ratón sobre un elemento.

¿Por qué es importante?

  • Feedback visual: El usuario sabe que el elemento es interactivo
  • Mejora la UX: Indica claramente los elementos clicables
  • Guía al usuario: Muestra qué elementos responden a la interacción

:focus - Cuando tiene el foco

La pseudo-clase :focus se activa cuando un elemento recibe el foco, generalmente al hacer clic en él o al navegar con el teclado (usando la tecla Tab).

¿Por qué es importante?

  • Accesibilidad: Los usuarios que navegan con teclado necesitan ver qué elemento está seleccionado
  • Formularios: Indica qué campo está activo para escribir
  • Navegación por teclado: Esencial para usuarios con discapacidades motoras

:focus en botones

Importante: Nunca elimines completamente los estilos de focus sin proporcionar una alternativa visual. Los usuarios que navegan con teclado los necesitan.

:active - Al hacer clic

La pseudo-clase :active se activa en el momento exacto en que el usuario está presionando un elemento (mientras mantiene el botón del ratón presionado).

¿Por qué es importante?

  • Feedback inmediato: El usuario sabe que su clic fue registrado
  • Sensación táctil: Simula la sensación de presionar un botón físico
  • Mejora la experiencia: Hace la interfaz más responsive y natural

Ejemplo completo con todas las pseudo-clases

.btn {
  background: #0066cc;
  color: white;
  padding: 12px 24px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: all 0.2s;

  /* Al pasar el ratón */
  &:hover {
    background: #0052a3;
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  }

  /* Al recibir el foco (con teclado) */
  &:focus {
    outline: 2px solid #0066cc;
    outline-offset: 2px;
  }

  /* Al hacer clic */
  &:active {
    background: #003d7a;
    transform: translateY(0);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  }
}

:disabled - Cuando está deshabilitado

La pseudo-clase :disabled se aplica a elementos de formulario que tienen el atributo disabled.

¿Por qué es importante?

  • Indica que no está disponible: El usuario sabe que no puede interactuar con el elemento
  • Previene acciones: Visualmente muestra que el elemento no responderá
  • Mejora la UX: Evita confusión sobre qué elementos son interactivos

Ejemplo básico

<button>Botón activo</button>
<button disabled>Botón deshabilitado</button>

<input type="text" placeholder="Campo activo" />
<input type="text" disabled placeholder="Campo deshabilitado" />
button {
  &:hover {
    background: darkblue;
  }

  &:disabled {
    background: #ccc;
    color: #666;
    cursor: not-allowed;
    opacity: 0.6;
  }

  /* Importante: hover NO debe aplicarse si está disabled */
  &:disabled:hover {
    background: #ccc; /* Se mantiene igual */
  }
}

input {
  border: 2px solid #ccc;
  padding: 10px;

  &:disabled {
    background: #f5f5f5;
    color: #999;
    cursor: not-allowed;
  }
}

La propiedad pointer-events: none

Cuando un elemento está deshabilitado, además de cambiar su apariencia visual, a veces queremos desactivar completamente todas las interacciones con ese elemento. Para esto usamos pointer-events: none.

¿Qué hace pointer-events: none?

La propiedad pointer-events controla si un elemento puede ser el objetivo de eventos del ratón o del puntero. Cuando se establece en none:

  • Ignora todos los clics: El elemento no responde a clics del ratón
  • Ignora hover: No se activará la pseudo-clase :hover
  • No captura eventos: Los eventos del ratón “atraviesan” el elemento
  • El cursor no cambia: No se mostrará el cursor definido en el elemento

Ejemplo práctico

<div class="container">
  <button class="disabled-with-pointer-events">No puedes hacer clic aquí</button>
</div>
.disabled-with-pointer-events {
  background: #ccc;
  color: #666;
  opacity: 0.6;
  cursor: not-allowed;

  /* Desactiva completamente las interacciones */
  pointer-events: none;

  /* Este hover nunca se aplicará gracias a pointer-events: none */
  &:hover {
    background: darkblue; /* No funcionará */
  }
}

¿Cuándo usar pointer-events: none?

/* Opción 1: Solo estilos visuales */
button {
  &:disabled {
    background: #ccc;
    cursor: not-allowed;
    opacity: 0.6;

    /* Necesitas sobrescribir el hover manualmente */
    &:hover {
      background: #ccc;
    }
  }
}

/* Opción 2: Con pointer-events (más simple) */
button {
  &:disabled {
    background: #ccc;
    cursor: not-allowed;
    opacity: 0.6;
    pointer-events: none; /* Ya no necesitas sobrescribir hover */
  }
}

Ventajas de pointer-events: none

  1. Más simple: No necesitas sobrescribir :hover, :active, etc.
  2. Más seguro: Garantiza que no habrá interacciones accidentales
  3. Mejor rendimiento: El navegador no procesa eventos en ese elemento

Consideraciones importantes

/* ⚠️ Cuidado: pointer-events afecta también a los elementos hijos */
.parent {
  pointer-events: none;

  /* Los hijos tampoco recibirán eventos */
  button {
    /* Este botón no responderá a clics */
  }
}

/* ✅ Solución: Reactiva eventos en hijos específicos */
.parent {
  pointer-events: none;

  .active-child {
    pointer-events: auto; /* Reactiva los eventos */
  }
}

Ejemplo completo: Botón con loading

<button class="btn-submit" disabled>
  <span class="spinner"></span>
  Enviando...
</button>
.btn-submit {
  background: #0066cc;
  color: white;
  padding: 12px 24px;
  border: none;
  cursor: pointer;

  &:hover {
    background: #0052a3;
  }

  &:disabled {
    background: #ccc;
    cursor: not-allowed;
    pointer-events: none; /* Desactiva toda interacción */

    /* Ya no necesitamos esto gracias a pointer-events: none */
    /* &:hover {
      background: #ccc;
    } */
  }
}

En resumen: pointer-events: none es una forma más robusta de asegurar que un elemento deshabilitado no responda a ninguna interacción del usuario.

Orden de las pseudo-clases

Cuando usas múltiples pseudo-clases en enlaces, el orden importa. La regla nemotécnica es LVHA (Love Hate):

a {
  color: blue;

  /* L - Link (estado normal del enlace) */
  &:link {
    color: blue;
  }

  /* V - Visited (enlace visitado) */
  &:visited {
    color: purple;
  }

  /* H - Hover (al pasar el ratón) */
  &:hover {
    color: darkblue;
  }

  /* A - Active (al hacer clic) */
  &:active {
    color: red;
  }
}

Si no sigues este orden, algunos estilos pueden no funcionar como esperas.

Otras pseudo-clases útiles

Además de las que hemos visto, hay otras pseudo-clases muy útiles:

/* Primer hijo */
li:first-child {
  font-weight: bold;
}

/* Último hijo */
li:last-child {
  border-bottom: none;
}

/* Elemento enésimo */
li:nth-child(odd) {
  background: #f5f5f5;
}

/* No (negación) */
button:not(.primary) {
  background: gray;
}

/* Válido/inválido (formularios) */
input:valid {
  border-color: green;

  &:invalid {
    border-color: red;
  }
}