Cómo mostrar un input inválido visualmente en CSS después de enviar un formulario

- 👤 Andrés Cruz

🇺🇸 In english

Cómo mostrar un input inválido visualmente en CSS después de enviar un formulario

Uno de los problemas más comunes cuando usamos validación HTML5 con CSS es que los campos obligatorios aparecen como inválidos desde el primer render, incluso antes de que el usuario haya interactuado con el formulario.
En formularios reales, esto no solo es confuso, sino que genera una mala experiencia de usuario.

En este artículo te explico por qué ocurre, cómo funciona realmente :invalid en HTML5 y, sobre todo, cómo mostrar errores visuales en los inputs solo después de hacer submit, usando CSS moderno y, si hace falta, un pequeño fallback con JavaScript.

¿Qué ocurre realmente cuando un input es :invalid en HTML5?

La validación de formularios en HTML5 funciona mediante el sistema de constraint validation. El navegador evalúa cada campo según sus atributos (required, type, pattern, etc.) y decide si es válido o no.

Cuándo se activa :invalid y por qué aparece al cargar la página

Un punto clave que suele sorprender es este:

Un <input required> vacío ya es inválido desde el momento en que la página carga.

Por eso, este CSS:

input:invalid {
  border: 2px solid red;
}

Provoca que los campos aparezcan en rojo incluso antes de escribir nada.

En mi caso, este fue el primer problema real que encontré: la validación funcionaba, pero el timing era pésimo.

El navegador no sabe si el usuario “aún no ha empezado” o si “ya se equivocó”. Para él, el campo simplemente no cumple las reglas.

Validación visual de campos del formulario con CSS

Con HTML5 podemos utilizar el atributo required para indicarle al navegador que este campo es requerido; en base a esto, podemos aprovecharnos de la presencia de dicho atributo en el elemento y hacer las validaciones más amigables para el usuario final con ayuda de CSS; veamos unos ejemplos.

<form>
  <input required />
  <input type="submit" />
</form>

Si el campo esta vacío e intentamos enviar el formulario; el navegador cancelara el envío del mismo y mostrará el siguiente mensaje:

Formulario mensaje

intenta enviar el formulario vacío escrito y verás el mensaje.

Ahora bien; si queremos personalizar los campos del formulario que poseen el atributo required (mostrarle un borde de color amarillo -advertencia- por ejemplo); podríamos utilizar una regla en CSS como esta:

/*Mostraremos los campos requeridos de color amarillo*/ 
form input:required { 
  border:2px solid yellow; 
  /* otras propiedades */ 
}

Diferencia entre :valid y :invalid

Podemos ir un paso más allá:

input:valid {
 border: 2px solid green;
}
input:focus:invalid {
 border: 2px solid red;
}

Aquí ya aparece una idea importante: condicionar :invalid al foco.

Ahora el campo solo se marcaba en rojo mientras el usuario estaba escribiendo.

Pero aún había un problema…

El problema UX: errores visibles antes de interactuar

Aunque :focus:invalid evita mostrar errores al cargar la página, no cubre el caso del submit.

Escenario típico:

  • El usuario no toca ningún campo
  • Pulsa “Enviar”
  • El navegador bloquea el envío
  • Pero visualmente… no pasa nada

Desde el punto de vista del usuario, el formulario “no funciona”.

Aquí entendí que el problema no era la validación, sino cuándo mostrar el feedback visual.

Por qué marcar inputs vacíos como inválidos es mala experiencia

Los errores deberían aparecer cuando:

  • El usuario intenta enviar
  • O cuando ya interactuó con un campo

Nunca antes.

Mostrar bordes rojos en inputs vacíos transmite una sensación de error constante, incluso cuando el usuario todavía no ha hecho nada mal.

Validación visual de campos del formulario con CSS

Podemos usar pseudo-class para hacer cosas realmente interesantes; por ejemplo podemos asignarle un color al input que el usuario está editando en un instante dado; rojo si el valor que esta colocando no es válido y verde si el valor que esta colocando el usuario es valido; para eso necesitamos la siguiente regla en CSS:

/*Si el valor que el usuario escribe es valido, obtendra un color verde*/
form input[type="email"]:required:valid{
 border:2px solid green;
 /* otras propiedades */
}
/*caso contrario, el color sera rojo*/
form input[type="email"]:focus:required:invalid{
 border:2px solid red;
 /* otras propiedades */
}

Y usaremos el siguiente formulario:

<form>
  <input required  type="email" />
  <input type="submit" />
</form>

Utilizamos la pseudo-class focus precediendo a la pseudo-class invalid porque cuando la página es cargada el campo por defecto se encuentra vacío; y por lo tanto tiene el estado invalid por defecto. Al aplicarle el estilo anterior al siguiente formulario.

Validación visual de campos del formulario con CSS

Por último; un ejemplo un poco mas completo el cual solicitará información personal.

Usando :focus:invalid

Es una mejora, pero limitada:

/*Mostraremos los campos requeridos de color amarillo*/
    form input:required {
       border:2px solid yellow;
    /* otras propiedades */
    }
    /*Si el valor que el usuario escribe es valido, obtendra un color verde*/
    form input:valid{
        border:2px solid green;
        /* otras propiedades */
    }
    /*caso contrario, el color sera rojo*/
    form input:focus:invalid{
        border:2px solid red;
        /* otras propiedades */
    }

Y usaremos el siguiente formulario:

<form>
    <input required type="text" name="nombre" placeholder="Primer nombre"/>
	<input type="text" name="nombre2" placeholder="Segundo nombre"/>
	<input required type="email" name="email" placeholder="Su correo electronico"/>
	<input required type="tel" name="tel" placeholder="Su teléfono"/>
	<input type="url" name="url" placeholder="Su pagina web" />
</form>

Como podemos apreciar hay campos que son obligatorios (primer nombre - email - teléfono) y otros que no son obligatorios (segundo nombre - página web) los primeros (los campos obligatorios) tendrán un borde de color amarillo y tendrán el mismo comportamiento que el formulario del ejemplo 2.

Usando :user-invalid (la solución correcta)

La pseudo-clase :user-invalid fue creada exactamente para este caso.

input:user-invalid {
 border: 2px solid red;
}

user-invalid solo se activa cuando:

  • El usuario ha interactuado con el campo o
  • Ha intentado enviar el formulario

Esto evita el clásico problema de inputs en rojo al cargar la página.
Cuando descubrí esta pseudo-clase, fue el punto de inflexión: el comportamiento encajaba exactamente con lo que esperaba como usuario.

:user-invalid es la mejor forma de mostrar errores solo después del submit usando CSS puro.

es una pseudo-clase moderna, compatible con navegadores actuales, pero no con versiones muy antiguas.

Compatibilidad y alternativas cuando :user-invalid no está disponible

Si necesitas compatibilidad total o más control, hay un fallback muy simple y efectivo.

Fallback con JavaScript y clase .submitted

JavaScript:

document.querySelector("form").addEventListener("submit", function () {
 this.classList.add("submitted");
});

CSS:

form.submitted input:invalid {
 border: 2px solid red;
}
input:invalid {
 border: 1px solid #ccc;
}

Este enfoque es muy robusto:

  • Antes del submit: estilos neutros
  • Después del submit: errores visibles

En proyectos reales, cuando necesito asegurar compatibilidad, sigo usando este patrón porque es claro, predecible y fácil de mantener.

Buenas prácticas de validación visual en formularios

  • Cuándo usar solo CSS
    • Formularios simples
    • Validación básica (required, email, pattern)
    • Navegadores modernos
    • UX clara sin mensajes personalizados
  • Cuándo necesitas JavaScript
    • Mensajes de error personalizados
    • Validaciones complejas
    • Compatibilidad legacy
    • Lógica condicional entre campos
  • Un buen enfoque es siempre:
    • HTML primero
    • CSS para el feedback visual
    • JavaScript solo como mejora

Preguntas frecuentes sobre validación de formularios con CSS

  • ¿Por qué :invalid se activa al cargar la página?
    • Porque un campo required vacío ya incumple la validación según HTML5.
  • ¿Cuál es la diferencia entre :invalid y :user-invalid?
    • :invalid depende solo de las reglas.
    • :user-invalid depende de la interacción del usuario.
  • ¿Se puede hacer validación visual sin JavaScript?
    • Sí, completamente, usando pseudo-clases CSS. JavaScript solo es necesario como fallback o para personalizar mensajes.
  • ¿Es recomendable ocultar errores hasta el submit?
    • Sí. En mi experiencia, mejora mucho la percepción del formulario y reduce frustración.

Conclusión

El verdadero reto de la validación de formularios no es validar, sino cuándo mostrar el error.

Usar :invalid sin contexto suele provocar una mala experiencia. En cambio:

  • :focus:invalid mejora el feedback en tiempo real
  • :user-invalid ofrece el comportamiento ideal tras el submit
  • Un pequeño fallback con JavaScript garantiza compatibilidad total

Si controlas el timing del feedback visual, tus formularios no solo funcionarán mejor, se sentirán mejor.

Aprende a realizar validaciones sobre los formularios con SOLO HTML y sin JavaScript.

Acepto recibir anuncios de interes sobre este Blog.

Descubre cómo controlar :invalid en CSS y mostrar errores solo tras el submit del formulario usando HTML5, :user-invalid y buenas prácticas UX.

| 👤 Andrés Cruz

🇺🇸 In english