Curso de CSS / formularios: foco, validación, disabled y readonly

CSS puede dar estilo a focus, validez y controles de formulario deshabilitados o de solo lectura, pero el HTML define sus etiquetas, restricciones y comportamiento de envío. :invalid puede coincidir antes de la interacción, mientras que :user-invalid ayuda a retrasar el estilo de error hasta que el navegador lo considere apropiado.

Asocia etiquetas e instrucciones con controles

Si el atributo for de label coincide exactamente con el id del campo, la etiqueta queda asociada al control. Los marcadores de posición desaparecen una vez que comienzas a escribir, por lo que no reemplazan los nombres o condiciones importantes. Para explicaciones adicionales, deja la oración visible y especifica el ID de la oración en aria-describedby.

<label for="email">Email address (required)</label>
<input id="email" name="email" type="email" required
  placeholder="name@example.com" aria-describedby="email-help">
<p id="email-help">Enter name@example.com in the same format.</p>

El código parcial anterior representa brevemente la sección de correo electrónico del ejemplo completo a continuación. id identifica el objeto en el documento, y name es el nombre del ítem en los datos enviados. Si name no está presente, no se incluirá en envíos normales, incluso si puedes escribirlo en pantalla. Para la composición de etiquetas y descripciones, consulta la guía de etiquetas de formulario de W3C y la guía de entrada de formulario de W3C sobre instrucciones de formulario.

Ajusta el tamaño de los controles para pantallas estrechas y texto más grande

Si solo dependes del ancho inherente de el campo de entrada, puede que se salga de una pantalla estrecha cuando se aumente el tamaño de la fuente. Establece el ancho máximo del formulario y usa width: 100% y box-sizing: border-box juntos en el campo de entrada. Esto asegurará que el borde y el relleno también estén incluidos dentro del ancho especificado.

*, *::before, *::after { box-sizing: border-box; }
form { max-width: 28rem; }
input { width: 100%; min-width: 0; padding: .65rem; font: inherit; }

Esta regla está incluida en la sección de estilo de todo el ejemplo. font: inherit permite que el campo de entrada y los botones sigan la fuente, el tamaño y la altura de línea del texto, y min-width ayuda a reducir el tamaño mínimo que impide la reducción incluso al insertar el campo de entrada en un Flexbox/Grid. No se utiliza una altura fija para que los elementos de abajo se muevan naturalmente hacia abajo incluso cuando se aumenta la descripción o se agrandan las letras.

Distingue el estado de foco y los errores de validación

:focus coincide con el elemento que actualmente tiene el foco, y :focus-visible coincide si el navegador determina que necesita mostrar el foco. Se muestra principalmente durante la navegación con teclado, pero también puede mostrarse al hacer clic con el ratón en el campo de entrada de texto, por lo que no debe entenderse como "se aplica solo al teclado." la descripción de :focus-visible de MDN explica los criterios del navegador . Para aplicar la indicación de foco de manera consistente también a los enlaces, compara estados de enlaces y foco visible de teclado con CSS.

El enfoque indica el elemento que estás manipulando actualmente, y los errores indican elementos que no cumplieron con las condiciones de entrada. Dado que ambos estados pueden estar presentes al mismo tiempo, todo el ejemplo muestra el enfoque con un contorno azul y el error con un borde rojo oscuro y una declaración de error. Deja algo de espacio alrededor del contorno para evitar truncarlo, y evita quitar el contorno y no proporcionar otro indicador de enfoque.

:invalid coincide si el valor actual es inválido, por lo que un campo de entrada requerido vacío también coincide desde el principio. :user-invalid coincide cuando el navegador mostrará una condición de error tras la interacción del usuario. Cuándo se muestra algo, como cuando terminas de escribir o cuando intentas enviar, puede depender del comportamiento de tu navegador. El laboratorio comprueba los errores presionando el botón Enviar. Consulta la descripción de :user-invalid de MDN .

Si necesitas admitir navegadores antiguos, comprueba si el selector es compatible y diseñar un flujo de verificación separado. Incluso si la decoración de error no aparece porque el selector no es compatible, esto no significa que desaparezcan las verificaciones requeridas y de formato de HTML. Elige un color de foco que sea suficientemente distinto del fondo real, y comprueba también las condiciones de aplicación de Guía de Contraste No Textual W3C.

Compara required, validez, disabled y readonly

situación significado Enfoque y envío general
required El usuario debe ingresar un valor Enfocable si el elemento está activo; Los valores vacíos están sujetos a validación al enviar
:valid·:invalid Si se cumplen las condiciones de validación de HTML Los resultados de la verificación se determinan independientemente de la forma del borde
disabled Actualmente no disponible Normalmente excluido de la navegación con Tab y de los envíos
readonly No se pueden modificar valores como la entrada de texto admitida Enfocable y seleccionable; Si existe un nombre, incluirlo en el envío

readonly es compatible con controles particulares similares a texto y no es una manera general de hacer que un elemento select sea solo lectura. Los controles de solo lectura compatibles están excluidos de la validación de restricciones, mientras que los controles nombrados todavía pueden enviar datos de formularios. Distingue ese comportamiento de los controles deshabilitados. Consulta HTML Standard: construcción de la lista de datos del formulario, MDN: readonly.

Este campo de correo electrónico demuestra un formato como name@example.com. Pasar su verificación de validez no confirma que la cuenta exista o pueda recibir correo. La validación HTML type="email" no acepta todos los formatos de direcciones internacionalizadas; las partes locales no ASCII no se presentan aquí como entrada garantizada válida. Para más detalles, véase condiciones de los campos de correo electrónico en HTML Standard.

Ejecuta el ejemplo e inspecciona los campos enviados

Guarda el siguiente código como form-states.html en UTF-8 y ábrelo en el navegador. HTML y CSS determinan el estado del formulario. El pequeño fragmento de JavaScript al final es un código auxiliar para interceptar envíos válidos y mostrar una lista de datos en la pantalla, sin enviarlos al servidor ni realizar ninguna verificación adicional de entrada. Si JavaScript está desactivado, la función de salida de resultados no funcionará.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Comparing form states and submitted fields</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
form { max-width: 28rem; }
.field { margin-block: 1.25rem; }
label { display: block; margin-bottom: .35rem; font-weight: 700; }
input { display: block; width: 100%; min-width: 0; padding: .65rem;
  border: 2px solid #6d7e8b; border-radius: .3rem; background: white; color: #183044; }
input:required { border-inline-start-width: 4px; }
input:user-invalid { border-color: #9d2632; }
.help, .error { margin-block: .4rem 0; }
.error { display: none; color: #9d2632; }
input:user-invalid + .error { display: block; }
input[readonly] { background: #eef6fa; }
input:disabled { background: #eceff2; color: #465766; cursor: not-allowed; }
button { padding: .65rem 1rem; border: 1px solid #164f73; border-radius: .3rem;
  background: #166b92; color: white; }
output { display: block; margin-block: 1rem; white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
<main>
<h1>Check input states and submitted data</h1>
<form id="demo-form">
  <div class="field">
    <label for="email">Email address (required)</label>
    <input id="email" name="email" type="email" autocomplete="email" required
      placeholder="name@example.com" aria-describedby="email-help email-error">
    <p id="email-error" class="error">Enter an email address in a format such as name@example.com; do not leave the field empty.</p>
    <p id="email-help" class="help">This sample input checks the address format. It does not check whether the example address receives mail.</p>
  </div>
  <div class="field">
    <label for="plan">Plan (read-only)</label>
    <input id="plan" name="plan" type="text" value="Basic" readonly>
  </div>
  <div class="field">
    <label for="coupon">Coupon (currently disabled)</label>
    <input id="coupon" name="coupon" type="text" value="WELCOME" disabled>
  </div>
  <button type="submit">Check submitted fields</button>
</form>
<output id="result" for="email plan coupon" aria-live="polite"></output>
<script>
  const form = document.getElementById("demo-form");
  form.addEventListener("submit", (event) => {
    event.preventDefault();
    const entries = Array.from(new FormData(form).entries());
    document.getElementById("result").textContent = JSON.stringify(entries, null, 2);
  });
</script>
</main>
</body>
</html>
  1. Deja vacío el correo y pulsa Check submitted fields. Comprueba el aviso de campo obligatorio del navegador y el mensaje de error del ejemplo.
  2. Introduce abc en el correo electrónico y vuelve a enviar. No se debería mostrar una nueva lista de resultados debido a un error de formato.
  3. Introduce name@example.com y envía. Los resultados incluyen correo electrónico y plan, pero no hay cupón deshabilitado.
  4. Usa Tab y comprueba el orden Email address → Plan → Check submitted fields. Los cupones inactivos se omiten en el orden normal de tabulación, y los planes de solo lectura pueden recibir foco.
  5. Elimina solo el atributo disabled del campo coupon. Comprueba que ahora recibe foco y que su valor WELCOME aparece en la lista de datos. Después restaura disabled.
  6. Elimina solo el atributo readonly del plan e intenta editarlo. Después de la comparación, restaura su valor a Basic y añade de nuevo readonly.
  7. Reduce el ancho de la pantalla y ajusta la ampliación del navegador o la configuración del tamaño de la fuente. Comprueba que el campo de entrada, el contorno de enfoque y las oraciones de error no estén cortadas ni superpuestas.

Comprende los límites de CSS y la validación del navegador.

CSS es responsable de mostrar el estado de un elemento. La verificación del navegador puede reducir los errores de entrada del usuario, pero puede ser eludida con herramientas de desarrollador o solicitudes separadas, por lo que el servicio real debe volver a verificar los valores permitidos, la longitud, los permisos, etc., en el servidor. Las oraciones de error describen específicamente los elementos problemáticos y las soluciones, y también se diseña un flujo separado para preservar la información de entrada cuando ocurre un error del servidor.

Si el ejemplo se comporta de manera inesperada, primero comprueba las parejas for/id, name, required, type y los estados disabled o readonly. Si el foco es invisible, comprueba si otra regla de contorno lo anula. Los errores mostrados de inmediato pueden provenir de :invalid. Si el mensaje permanece oculto después de un intento de envío, inspecciona la compatibilidad de :user-invalid y la selección explicada en combinadores y pseudoclases de CSS.