Aprovecha required, pattern, inputmode y la Constraint Validation API para formularios robustos sin ninguna librería externa.
Abrir esta lección en KodokonEl navegador incluye un motor de validación completo que muchos equipos reimplementan en JavaScript por costumbre. Los bloques básicos: required (campo obligatorio), los tipos (email, url, date) que validan el formato, minlength/maxlength, min/max/step para los valores numéricos, y pattern, que acepta una expresión regular. Detalle crucial: pattern está anclado implícitamente - el valor entero debe coincidir, como si la expresión regular estuviera envuelta en ^ y $. Añade autocomplete con los valores estandarizados (postal-code, email, tel): es a la vez UX y accesibilidad.
<label for="zip">Postal code</label>
<input id="zip" name="zip" type="text"
required pattern="[0-9]{5}"
inputmode="numeric"
autocomplete="postal-code">El atributo inputmode se malinterpreta a menudo: no valida nada. Se limita a indicar al sistema móvil qué teclado virtual mostrar (numeric, tel, email, decimal, search). Por eso la combinación type="text" + inputmode="numeric" + pattern es el patrón recomendado para los códigos numéricos: obtienes el teclado adecuado sin heredar los comportamientos de type="number".
input:user-invalid {
border: 2px solid #b3261e;
}
input:user-valid {
border: 2px solid #1b5e20;
}Del lado de CSS, prefiere :user-invalid frente a :invalid: este último se aplica en cuanto se carga la página, pintando de rojo campos que el usuario ni siquiera ha tocado. Del lado de JavaScript, la Constraint Validation API completa el conjunto de herramientas: checkValidity() dispara la comprobación, el objeto validity expone la causa exacta (patternMismatch, valueMissing, tooShort) y setCustomValidity() te permite sustituir los mensajes genéricos del navegador por los tuyos.
const zip = document.querySelector("#zip");
zip.addEventListener("input", () => {
if (zip.validity.patternMismatch) {
zip.setCustomValidity("Enter 5 digits.");
} else {
zip.setCustomValidity("");
}
});