Lo que el navegador ya da —foco, teclado, semántica, autofill— se usa tal cual. Aquí solo se escribe lo que NO da: la etiqueta obligatoria, el error en texto y un borde que se vea.
HTML nativo sobre tokens: un <input> y nada más. Cero librería — el navegador ya da foco, teclado, semántica, autofill y participación en el formulario. Lo que el navegador NO da es lo que se escribe aquí: la etiqueta obligatoria, el error en texto y un borde que llegue a 3:1.
La ayuda NO desaparece cuando aparece el error — sigue atada con aria-describedby.
Reposo, sin ayuda ni error.
Etiqueta OCULTA, no ausente: el lector la sigue anunciando.
Solo lectura: se puede enfocar y copiar, y el borde retrocede.
Deshabilitado: fuera del tabulador.
El navegador pone su propio ojo y su gestor de contraseñas.
`action` son props con nombre, no un ReactNode: el paquete pone el `<button>`.
El botón es un IconButton de 24×24 (el mínimo de SC 2.5.8, sin necesitar la excepción de Spacing), lo pone el paquete con type="button" —sin eso, dentro de un <form> el ojo envía el formulario— y vive FUERA del <label>, así que no le roba el nombre al campo. label="" no compila.
El <textarea> con la misma etiqueta, ayuda y error que Field: los dos comparten un armazón interno, así que el cableado de accesibilidad no se puede separar entre ellos. Es un hermano, no un envoltorio genérico — es lo que hace Carbon, cuyo TextArea lleva sus propias labelText e invalidText en vez de delegarlas.
Se guarda en la bitácora de auditoría.
La política no puede quedar vacía.
Solo lectura: se enfoca y se copia.
Se puede estirar en VERTICAL, no en horizontal: `resize: both` dejaría al usuario romper la fila.
Ya no es un <select> nativo (cambió el 2026-08-18). Es el mismo control que el Combobox —misma altura, mismo borde, mismo galón, misma losa de opciones con su palomita— pero sin escribir para filtrar: el patrón Select-Only Combobox de la APG. Pruébalo con el teclado: flechas para recorrer, Home/End a los extremos, Enter o Espacio para abrir y elegir, Escape para salir sin cambiar nada y Tab que cierra sin quedarse con lo resaltado. Y teclea pr seguido en el primero: acumula las letras como el nativo y reinicia tras un segundo de pausa.
🔴 Lo que se perdió al dejar de ser nativo, dicho de frente: en el teléfono ya no sale el selector del sistema, así que el desplazamiento, el tamaño de los objetivos táctiles (44 px por opción con el dedo) y el teclado los ponemos nosotros. A cambio, la lista abierta —lo único que el nativo no dejaba estilar— por fin es nuestra, y los dos desplegables del kit se ven por fin iguales.
Determina qué puede hacer dentro de la aplicación
Elige un estado antes de continuar
🔴 La capa de estado de app/globals.css no alcanza a un <input> — su selector es button, [role=…], a[href], [data-press]. Así que un campo no lleva ni hover ni elevación; lo que sí le llega es el anillo de :focus-visible, y un outline-none aquí lo borraría.
Arrastra un archivo encima. Verde y latiendo = entra. Rojo y temblando = no. Y hay un tercer estado que parece un descuido y no lo es: durante un arrastre el navegador no da el nombre del archivo, solo su tipo MIME. Con eso, un accept por extensión (.pdf) alcanza para descartar —una imagen no va a llamarse .pdf— pero nunca para confirmar: el nombre podría ser cualquier cosa. De ahí la asimetría, y las dos tarjetas de abajo la enseñan: por extensión hay rojo, no hay verde, y queda neutro lo que de verdad no se puede saber.
🔑 Lo dice un maintainer de Atlassian sobre la propia plataforma: «you can know what 'types' are being dragged during a drag, but you can only see what data is being dragged during a successful 'drop' event». Por eso Carbon ni siquiera intenta el rechazo durante el arrastre, y react-dropzone expone un estado unknown, que es el que aquí se pinta neutro.
🗺️ El rojo por extensión sale de una tabla de extensión → MIME que nunca estará completa, porque ese MIME lo pone el sistema operativo y depende de qué tenga instalado el usuario: en Windows un .csv llega como application/vnd.ms-excel si hay Excel. Así que lo desconocido —una extensión que no está en la tabla, un MIME que no reconocemos, o el application/octet-stream con el que la especificación manda decir «no sé»— cae en neutro, nunca en rojo. Y no es una cortesía: un rojo apaga el drop, así que un falso rojo bloquea un archivo bueno.
♿ El arrastre nunca es el único camino: el botón abre el selector nativo y está en el recorrido del teclado. Y con prefers-reduced-motion se apagan las dos animaciones — el color y el borde siguen contando la historia.
Elegir entre pocas opciones excluyentes. Es un grupo de radio, no dos botones de alternancia: un lector de pantalla anuncia que elegir uno apaga el otro, y el teclado se comporta como debe — una sola parada de tabulación para el grupo, y dentro se mueve con flechas. Con dos botones sueltos son dos paradas y las flechas no hacen nada.
🔴 Nació de un defecto real: en la pantalla de un ticket de soporte no se distinguía si estabas escribiendo una nota interna o una respuesta al cliente — y de eso depende si el cliente lo ve. Eran dos botones con énfasis secondary y ghost, que de un vistazo se parecen. Aquí el seleccionado lleva tres señales: relleno propio, tinta de marca y sombra — el color por sí solo no vale (WCAG 1.4.1).