Back

Estilización de Estados Válidos e Inválidos de Formularios con CSS

Estilización de Estados Válidos e Inválidos de Formularios con CSS

Campos obligatorios vacíos que brillan en rojo antes de que los usuarios escriban un solo carácter. Esa es la experiencia frustrante que muchos formularios ofrecen, y es completamente evitable con CSS moderno.

Esta guía cubre los estados de validación de formularios CSS y las pseudo-clases que los controlan. Aprenderás cuándo usar :valid e :invalid, por qué :user-valid y :user-invalid proporcionan mejor UX, y cómo estilizar errores de formularios con CSS usando patrones como :has() para estilizar elementos padre.

Puntos Clave

  • La validación por restricciones de HTML5 funciona automáticamente con atributos como required, type, min, max y pattern
  • Evita :valid e :invalid para estilizar errores ya que se activan antes de la interacción del usuario
  • Usa :user-valid y :user-invalid para mostrar retroalimentación de validación solo después de que los usuarios interactúen con los campos
  • Combina :has() con pseudo-clases de validación para estilizar elementos padre y controlar la visibilidad de mensajes de error
  • Siempre combina la validación visual con atributos ARIA para la accesibilidad de lectores de pantalla

Cómo Funciona la Validación por Restricciones

La validación por restricciones de HTML5 ocurre automáticamente. Cuando añades atributos como required, type="email", min, max, minlength, maxlength o pattern a campos de formulario, los navegadores evalúan la validez continuamente.

El navegador verifica las restricciones cada vez que el valor del input cambia. Las pseudo-clases CSS reflejan este estado de validez en tiempo real, permitiéndote estilizar campos según si pasan o fallan la validación.

<input type="email" required minlength="5" />

Este campo se vuelve inválido si está vacío, si el valor no tiene formato de email, o si tiene menos de cinco caracteres.

Pseudo-Clases Principales de Formularios CSS

:valid e :invalid

Estas pseudo-clases coinciden con campos basándose en la validez actual:

input:valid {
  border-color: green;
}

input:invalid {
  border-color: red;
}

El problema: :invalid se aplica inmediatamente al cargar la página. Un campo obligatorio sin valor es técnicamente inválido antes de que el usuario lo toque. Esto crea una mala experiencia: los usuarios ven errores antes de haber hecho algo mal.

:required y :optional

Estas apuntan a campos basándose en el atributo required:

input:required {
  border-left: 3px solid blue;
}

input:optional {
  border-left: 3px solid gray;
}

Útiles para indicadores visuales que muestran qué campos deben completarse.

:in-range y :out-of-range

Para inputs de tipo number y range con restricciones min/max:

input:in-range {
  background: white;
}

input:out-of-range {
  background: #ffe0e0;
}

Mejor UX con :user-valid y :user-invalid

Las pseudo-clases :user-valid y :user-invalid resuelven el problema de errores prematuros. Solo coinciden después de que el usuario ha interactuado con el campo, típicamente después de editar y perder el foco.

input:user-invalid {
  border-color: red;
}

input:user-valid {
  border-color: green;
}

Ahora los campos obligatorios no muestran errores al cargar la página. La estilización de validación aparece solo después de que los usuarios han tenido la oportunidad de ingresar datos. Esto coincide con las expectativas del usuario y reduce la frustración.

Estas pseudo-clases tienen amplio soporte en navegadores y deberían ser tu elección predeterminada para estilizar errores de formularios con CSS.

Estilización de Elementos Padre con :has()

El selector :has() permite estilizar elementos padre basándose en estados de validación de hijos, algo previamente imposible sin JavaScript.

<div class="field">
  <label for="email">Email</label>
  <input type="email" id="email" required />
  <span class="error">Por favor ingresa un email válido</span>
</div>
.error {
  display: none;
  color: red;
}

.field:has(input:user-invalid) .error {
  display: block;
}

.field:has(input:user-invalid) input {
  border-color: red;
}

Este patrón muestra mensajes de error solo cuando el input contenido falla la validación después de la interacción del usuario. El contenedor padre controla la visibilidad de elementos de error hermanos.

Consideraciones de Accesibilidad

La estilización visual por sí sola no es suficiente para formularios accesibles. Los usuarios de lectores de pantalla necesitan asociaciones de errores programáticas.

Combina estados de validación CSS con atributos ARIA apropiados:

<div class="field">
  <label for="email">Email</label>
  <input 
    type="email" 
    id="email" 
    required
    aria-describedby="email-error"
    aria-invalid="false"
  />
  <span id="email-error" class="error" aria-live="polite">
    Por favor ingresa un email válido
  </span>
</div>

Requisitos clave de accesibilidad:

  • aria-describedby vincula el input a su mensaje de error
  • aria-live="polite" anuncia cambios de error a lectores de pantalla
  • aria-invalid debe actualizarse vía JavaScript cuando la validación falla
  • Contraste de color debe cumplir con los requisitos WCAG (4.5:1 para texto)
  • No confíes solo en el color: usa iconos o texto junto con cambios de color

Patrón Práctico: Validación Completa de Campos

Aquí hay un patrón listo para producción que combina estas técnicas:

/* Estilos base del campo */
.field input {
  border: 2px solid #ccc;
  transition: border-color 0.2s;
}

/* Estado válido después de interacción */
.field:has(input:user-valid) input {
  border-color: #2e7d32;
}

/* Estado inválido después de interacción */
.field:has(input:user-invalid) input {
  border-color: #c62828;
}

.field:has(input:user-invalid) .error {
  display: block;
}

/* Mensaje de error oculto por defecto */
.error {
  display: none;
  color: #c62828;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

Este enfoque mantiene los mensajes de error ocultos hasta que los usuarios interactúan con los campos, luego los revela solo cuando la validación falla.

Conclusión

Las pseudo-clases modernas de formularios CSS proporcionan una poderosa estilización de validación sin JavaScript. Usa :user-valid y :user-invalid como tus herramientas principales: previenen la visualización prematura de errores y coinciden con las expectativas del usuario. Combínalas con :has() para estilización a nivel de padre que controla la visibilidad de mensajes de error.

Recuerda que la estilización visual complementa pero no reemplaza el manejo accesible de errores. Siempre combina estados de validación CSS con atributos ARIA apropiados para usuarios de lectores de pantalla.

Preguntas Frecuentes

La pseudo-clase :invalid coincide con cualquier campo que falla la validación, incluso al cargar la página antes de la interacción del usuario. La pseudo-clase :user-invalid solo coincide después de que el usuario ha interactuado con el campo y lo ha abandonado. Esto previene mostrar estilos de error en campos obligatorios vacíos antes de que los usuarios tengan la oportunidad de completarlos.

Estas pseudo-clases tienen amplio soporte en navegadores modernos incluyendo Chrome, Firefox, Safari y Edge. Para soporte de navegadores más antiguos, considera usar las pseudo-clases :focus y :not(:placeholder-shown) como patrón alternativo, aunque este enfoque es menos preciso.

Las pseudo-clases de validación CSS reflejan el estado de la API de validación por restricciones del navegador. Si previenes el envío predeterminado del formulario o usas JavaScript para manejar formularios, asegúrate de llamar checkValidity() en los elementos del formulario. Los estados CSS se actualizan basándose en la propiedad validity, no en intentos de envío.

Usa el selector :has() en un contenedor padre para detectar estados de validación de inputs hijos. Por ejemplo, .field:has(input:user-invalid) .error-message te permite mostrar u ocultar elementos de error hermanos basándose en si el input dentro de ese contenedor falla la validación después de la interacción del usuario.

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay