Consigna y contexto aplicable
Construye el flujo de validación para un formulario de pago que contiene correo electrónico, país, código postal, fecha de entrega y un código de descuento opcional. Las reglas del código postal dependen del país. El código de descuento es verificado por un servicio remoto. El servidor sigue siendo la autoridad definitiva para los precios, el stock, las reglas de dirección y la validez del código.
El formulario debe funcionar con teclado y tecnología de asistencia, con un zoom del 200% y después de un viaje de ida y vuelta al servidor (server round trip). Los errores no pueden transmitirse únicamente mediante el color. Un envío fallido debe preservar los valores ingresados, identificar cada error subsanable y proporcionar una vía directa de recuperación. Las fallas de red, un código vencido y una respuesta asíncrona más reciente que supera a una más antigua son parte del problema.
El objetivo no es inventar una biblioteca de formularios. La respuesta debe definir el estado, los tiempos de validación, las relaciones semánticas, la política de foco y el contrato de errores entre cliente y servidor, para luego mostrar cómo se verifican esas decisiones. Las restricciones nativas de HTML son la base; el comportamiento personalizado debe aportar claridad sin eliminar la semántica del navegador.
Qué evalúa el entrevistador
La primera señal es la responsabilidad. La validación del lado del cliente proporciona una retroalimentación rápida y evita solicitudes innecesarias. La validación del servidor decide si se acepta un pedido. Un candidato que confía en un atributo required del lado del cliente o en el total del descuento ha dejado una brecha de seguridad y corrección.
La segunda señal es la recuperación, no la detección. Un borde de color de error es insuficiente. Cada error necesita texto claro, una relación programática con su control y una instrucción de corrección. Al enviar el formulario, los usuarios necesitan una vista general y un lugar predecible para el foco. Sus entradas válidas deben permanecer intactas.
La tercera señal son los tiempos. Mostrar "inválido" mientras el usuario todavía está escribiendo un correo electrónico genera ruido. Valida primero al enviar; después de que un campo haya fallado, reválídalo al perder el foco (blur) o tras un cambio significativo para que el usuario pueda ver que la corrección funcionó. La validación asíncrona necesita un estado pendiente y protección contra respuestas obsoletas.
La señal final es la verificación. Un escaneo de accesibilidad automatizado puede encontrar etiquetas faltantes, pero no puede demostrar anuncios sensatos, orden de foco, recuperación tras una respuesta del servidor o protección contra una respuesta de código de descuento fuera de orden. Todo ello requiere pruebas de interacción.
Preguntas para aclarar antes de responder
- ¿Cuáles comprobaciones son locales? La presencia, el formato y las reglas simples entre campos pueden ejecutarse localmente. El precio, el stock, la elegibilidad y la validez final del descuento pertenecen al servidor.
- ¿Cuándo debe aparecer la retroalimentación? El envío revela todos los errores bloqueantes. Un campo que ya ha fallado puede revalidarse al perder el foco (blur) o después de un cambio deliberado; no anuncies en cada pulsación de tecla.
- ¿Volverá el servidor a renderizar la página? Tanto el envío de formulario mejorado como el ordinario deben preservar los valores y renderizar los mismos errores estructurados.
- ¿Puede un solo error afectar a varios controles? El país y el código postal forman una sola regla. Coloca las instrucciones compartidas cerca del grupo y vincula el resumen al control que inicia la corrección.
- ¿Qué sucede durante la validación del descuento? Su estado pendiente es informativo, no un error. El valor actual y la generación de la solicitud deciden si una respuesta sigue siendo relevante.
- ¿A dónde debe moverse el foco tras un fallo? Para varios errores, enfoca un resumen antes del formulario; para un caso compacto de un solo error, enfocar el control inválido puede ser aceptable. Elige una política y evita anunciar el mismo cambio dos veces.
- ¿Qué fallos no son errores de campo? Una interrupción de red o un cambio de stock pertenecen a un mensaje a nivel de formulario con una opción de reintento. No lo asocies al correo electrónico o al código postal.
- ¿Expone el flujo datos sensibles? Los formularios de autenticación y recuperación de cuenta pueden necesitar mensajes genéricos del servidor. Una corrección útil no debe revelar si una cuenta existe.
Estructura de respuesta de 30 segundos
“Comienzo con controles nativos etiquetados y restricciones, pero el servidor sigue siendo la autoridad definitiva. Cada control inválido recibe aria-invalid="true" y un error de texto estable referenciado por aria-describedby; el color y los íconos son secundarios. El primer envío fallido renderiza un resumen de errores con enlaces a cada campo inválido y mueve el foco a ese resumen. Los valores ingresados permanecen en su lugar.
Valido al enviar y luego, al perder el foco (blur) o tras un cambio significativo, solo para los campos que han fallado. Las comprobaciones de códigos de descuento se aplican con debounce, son cancelables y se etiquetan con una generación para que una respuesta antigua no pueda reemplazar un valor nuevo. El servidor devuelve errores de campo conocidos más errores a nivel de formulario en una estructura definida. Pruebo la recuperación mediante teclado y lectores de pantalla, el zoom y colores forzados, los viajes de ida y vuelta al servidor, el envío con JavaScript deshabilitado y las condiciones de carrera asíncronas, además de las comprobaciones automatizadas.”
Análisis detallado paso a paso
Paso 1: Definir el estado y los invariantes
Mantén los valores separados del estado de validación. Cada campo puede estar sin tocar, pendiente, válido o inválido, con un código de error que se mapea a texto localizado. Los errores a nivel de formulario van por separado. Almacena una bandera de intento de envío para que la interfaz de usuario no presente un campo a medio escribir como fallido.
Los invariantes son: las etiquetas permanecen visibles; las instrucciones existen antes de un error; cada error de campo mostrado está asociado programáticamente; no se hace referencia a errores ocultos; un envío produce un único movimiento de foco claro; los datos válidos sobreviven al fallo; y una respuesta asíncrona obsoleta no puede mutar el estado actual.
Paso 2: Construir controles semánticos antes de ARIA
Utiliza label, el tipo de entrada más específico, required, autocomplete y las restricciones de longitud o patrón apropiadas. Una regla de código postal dependiente del país puede usar setCustomValidity(). Limpia el mensaje personalizado con una cadena vacía tan pronto como el valor sea válido; de lo contrario, el navegador continuará bloqueando el envío.
<label for="email">Email</label>
<input id="email" name="email" type="email"
aria-invalid="true"
aria-describedby="email-hint email-error">
<p id="email-hint">name@example.com</p>
<p id="email-error">Enter a valid email address.</p>checkValidity() verifica las restricciones, mientras que reportValidity() también solicita al navegador que presente su retroalimentación. Si el producto renderiza sus propios mensajes accesibles, intercepta el estado inválido de manera consistente en lugar de mostrar dos sistemas de error que compitan entre sí.
Paso 3: Elegir deliberadamente los tiempos de validación
En el primer envío, valida cada campo y revela todos los errores bloqueantes. Después de eso, reválida un campo inválido al perder el foco (blur). Para selecciones o un valor corregido con un formato completo e inequívoco, un cambio puede limpiar el error antes. No envíes repetidamente anuncios mediante regiones activas (live regions) mientras una fecha o un correo electrónico estén incompletos.
Las reglas entre campos se ejecutan cuando cualquiera de las dependencias cambia. Si el país cambia, reválida el código postal y actualiza su instrucción visible. No reescribas silenciosamente la entrada del usuario a menos que la normalización sea segura y reversible; recortar los espacios en blanco circundantes es diferente de adivinar un formato postal.
Paso 4: Presentar errores en línea y un resumen navegable
Renderiza texto conciso junto a cada campo, establece aria-invalid solo mientras sea inválido y referencia el ID estable del error. Mantén el tratamiento visual utilizable en modo de colores forzados e incluye palabras, no solo bordes rojos o íconos de advertencia.
Después de un envío fallido con múltiples errores, inserta un resumen con título antes del formulario. Dale un destino de foco temporal, enfócalo una sola vez, indica la cantidad de errores y proporciona enlaces a los controles relacionados. El texto del enlace debe nombrar el campo y la corrección. No muevas también el foco al primer campo en el mismo evento; eso dificultaría la inspección del resumen.
Paso 5: Hacer que la validación asíncrona sea segura contra condiciones de carrera
Espera a que el código de descuento esté completo, luego aplica debounce a la solicitud. Cancela (abort) la solicitud anterior cuando el valor cambie y compara también una generación que aumente monótonamente al completarse. Ambas comprobaciones importan porque la cancelación puede llegar después de que una respuesta ya haya progresado.
Muestra un estado cercano de “Verificando” a través de una región activa cortés (polite live region). Deshabilita la aplicación del descuento mientras la verificación esté pendiente, pero no deshabilites campos no relacionados. Un tiempo de espera agotado se convierte en un estado reintentable a nivel de formulario o a nivel de código de acuerdo con el contrato del producto; no debe reportarse como “código inválido”. Almacena en caché los resultados únicamente con el contexto de precios y la expiración que los hagan válidos.
Paso 6: Conciliar los errores autoritativos del servidor
Envía los valores sin procesar mediante la acción ordinaria del formulario o una solicitud mejorada. El servidor normaliza y valida nuevamente, calcula el total y devuelve un resultado como códigos de error de campo, códigos de error de formulario y los valores aceptados. El cliente mapea únicamente nombres de campo permitidos en una lista blanca. Las claves desconocidas se convierten en un error seguro a nivel de formulario en lugar de una oportunidad de inyección de selectores o marcado.
En caso de rechazo, preserva todas las entradas no sensibles, reemplaza las suposiciones del cliente con la verdad del servidor, renderiza el mismo resumen y enfócalo. En caso de éxito, muestra una confirmación inequívoca y evita la activación duplicada. Si la respuesta se pierde después del envío, utiliza una clave de idempotencia o una búsqueda de orden antes de alentar otro cobro.
Paso 7: Verificar las vías de recuperación, no instantáneas (snapshots)
Prueba el envío vacío, un error, varios errores, la dependencia país/código postal, fecha de entrega vencida, tiempo de espera del descuento agotado, descuento inválido, cambios rápidos de código, rechazo exclusivo del servidor y respuesta de éxito perdida. Afirma que los valores permanezcan, que los enlaces del resumen enfoquen los controles correctos, que los errores corregidos desaparezcan y que las respuestas asíncronas antiguas sean ignoradas.
Completa manualmente el flujo usando solo el teclado y un lector de pantalla. Comprueba el zoom al 200%, el reflujo (reflow), el foco visible, los colores forzados, el autocompletado del navegador y el envío ordinario al servidor sin JavaScript en el cliente. Las pruebas automatizadas de accesibilidad y unitarias complementan estas comprobaciones; no reemplazan las pruebas de anuncios y de foco.
Ejemplo de respuesta sólida
“Modelaría los valores de los campos de forma independiente del estado de tocado, pendiente y error. Las etiquetas nativas, los tipos de entrada, los tokens de autocompletado y las restricciones proporcionan la base. Una regla personalizada entre campos usa setCustomValidity() y siempre limpia el mensaje cuando es válida. Las comprobaciones del cliente mejoran la velocidad; el servidor revalida precios, stock, direcciones y descuentos antes de aceptar el pedido.
El primer envío revela cada error bloqueante. Cada entrada inválida recibe aria-invalid="true" y hace referencia al texto correctivo visible con aria-describedby. Para múltiples errores renderizo un resumen antes del formulario, lo enfoco una vez y vinculo cada elemento a su control. Los valores válidos permanecen intactos. Después de ese envío, los campos fallidos se revalidan al perder el foco (blur) o tras un cambio significativo, sin un anuncio en vivo en cada pulsación de tecla.
La validación del descuento tiene debounce y lleva tanto una señal de cancelación como un número de generación. Solo la respuesta que coincide con el valor actual puede actualizar la interfaz de usuario. Pendiente y no disponible son distintos de inválido. Los códigos de campo del servidor se mapean a través de una lista de permitidos; los fallos globales permanecen a nivel de formulario. Lo lanzaría a producción solo después de que las pruebas de teclado, lector de pantalla, zoom, viaje de ida y vuelta al servidor sin scripts, condiciones de carrera y envíos duplicados pasen junto a las comprobaciones automatizadas.”
Errores comunes
- Usar solo un borde rojo → algunos usuarios no pueden percibir el estado → agrega texto correctivo, asociación programática y una señal visual que no dependa solo del color.
- Validar en cada pulsación de tecla → la entrada incompleta genera ruido constante → valida al enviar y luego revalida los campos fallidos en un límite útil.
- Dejar
setCustomValidity()poblado → la entrada corregida sigue siendo inválida → establécelo en una cadena vacía cuando la regla se cumpla. - Enfocar el resumen y el primer campo → los anuncios y el foco compiten → haz un único movimiento de foco deliberado y proporciona enlaces para la navegación.
- Tratar un tiempo de espera agotado como un código inválido → una falla de infraestructura se convierte en una falsa culpa para el usuario → representa pendiente, no disponible e inválido por separado.
- Aceptar la última respuesta recibida → una validación obsoleta sobrescribe la entrada actual → cancela el trabajo antiguo y compara las generaciones de solicitud.
- Confiar en los totales del cliente → las solicitudes pueden eludir la interfaz de usuario → recalcula y valida en el servidor.
- Limpiar el formulario después de un fallo → la recuperación se convierte en volver a escribir todo → preserva los valores válidos y no sensibles, y reemplaza únicamente el estado de error.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Debería el resumen usar role="alert"?
Para errores insertados dinámicamente, una alerta o una región activa adecuada puede anunciar el resumen. El foco también puede hacerlo accesible. Prueba la combinación elegida porque el foco más una alerta asertiva pueden anunciar el mismo contenido dos veces. En una navegación completa del servidor, colocar el conteo de errores en el título de la página y en el encabezado principal puede proporcionar contexto previo.
Pregunta de seguimiento 2: ¿Por qué no deshabilitar el botón de envío hasta que el formulario sea válido?
Un botón deshabilitado puede ocultar lo que sigue estando mal y es inaccesible para algunos modos de navegación. Mantén una vía de envío disponible para que el usuario pueda solicitar un resultado de validación completo. Deshabilítalo únicamente mientras un envío real esté en curso, explica ese estado y recupérate si falla.
Pregunta de seguimiento 3: ¿Cuándo es preferible aria-describedby a una región activa?
aria-describedby proporciona al campo su sugerencia y error actuales cuando el usuario llega a él. Una región activa anuncia un cambio dinámico significativo sin necesidad de foco. Los errores estáticos en línea no necesitan todos regiones activas; anunciar cada campo a la vez resulta ruidoso. Usa un resumen para el conjunto y un estado cortés para los cambios verdaderamente asíncronos.
Pregunta de seguimiento 4: ¿Cómo das soporte a la validación renderizada por el servidor sin JavaScript?
Utiliza una acción de formulario real. El servidor devuelve la misma página con los valores preservados, los códigos de error de campo, un resumen antes del formulario y un conteo de errores en el título o encabezado. Los enlaces del resumen utilizan IDs de control estables. La mejora del lado del cliente debe consumir el mismo contrato de error, de modo que los dos modos no diverjan.
Pregunta de seguimiento 5: ¿Cómo probarías la condición de carrera asíncrona de manera determinista?
Controla dos promesas de validación. Inicia la solicitud A, cambia el valor y luego inicia B. Resuelve B como válida y A más tarde como inválida. Asegura que la interfaz de usuario siga reflejando B y el valor actual. Repite con cancelación (abort), tiempo de espera agotado, desmontaje (unmount) y reenvío mientras está pendiente.