Lección 6 de 10 · 14 min de lectura · Revisado el 26/10/2026
Formularios, teclados y autenticación accesible en apps
Darse de alta, iniciar sesión, pedir una cita, firmar una operación, pagar: casi todas las tareas críticas de una app pasan por un formulario. En el móvil, además, se escribe con un teclado en pantalla que tapa media interfaz, se copian códigos que llegan por SMS y se combinan contraseñas, biometría y gestores de credenciales. En el curso de formularios web del nivel 3 viste los fundamentos (etiquetas, errores, autocomplete, 3.3.7 y 3.3.8). Esta lección los traslada a las API nativas de iOS, Android y los frameworks multiplataforma, con los errores que más se repiten en apps de banca, administración y comercio electrónico.
Etiquetas visibles y persistentes
El patrón más extendido en móvil es el campo con placeholder (texto de ejemplo dentro del campo) como única etiqueta. Al escribir, el texto desaparece y la persona con problemas de memoria o atención ya no sabe qué campo está rellenando; además, el placeholder suele tener poco contraste. Puede incumplir 3.3.2 Etiquetas o instrucciones en cuanto el campo tiene contenido, y con frecuencia 1.4.3 Contraste (mínimo).
- Android:
TextInputLayoutconandroid:hint(Views) o el parámetrolabeldeOutlinedTextFielden Compose Material 3; ambos convierten el texto en etiqueta flotante que permanece visible y se expone como nombre accesible. El parámetroplaceholderes solo un ejemplo adicional. - iOS: coloca una etiqueta visible encima del campo y asígnala como
accessibilityLabeldelUITextField; en SwiftUI, combina unTextvisible conTextFieldy aplica.accessibilityLabelsi el título del campo no coincide con la etiqueta visible. - Coherencia con la etiqueta visible: 2.5.3 Etiqueta en el nombre exige que el nombre accesible contenga el texto visible. Si se ve «Código postal», el nombre no puede ser «CP» ni «campo 4»; Voice Control y Acceso por voz dependen de ello.
Tipo de teclado y propósito de la entrada
Elegir el teclado adecuado no es un criterio WCAG por sí mismo, pero reduce errores y esfuerzo para todo el mundo: teclado numérico para el código postal, de correo para el email, de teléfono para el móvil. Más importante es el criterio 1.3.5 Identificar el propósito de la entrada (AA): los campos que piden datos de la propia persona deben exponer su propósito de forma que el sistema pueda autocompletarlos. En apps, WCAG2ICT lo interpreta a través de los mecanismos de autocompletado de la plataforma.
// iOS (UIKit)
correoField.keyboardType = .emailAddress
correoField.textContentType = .emailAddress
cpField.keyboardType = .numberPad
cpField.textContentType = .postalCode
// SwiftUI
TextField("Correo electrónico", text: $correo)
.keyboardType(.emailAddress)
.textContentType(.emailAddress)
.textInputAutocapitalization(.never)
// Android (Views, XML)
<EditText
android:id="@+id/correo"
android:inputType="textEmailAddress"
android:autofillHints="emailAddress" />
// Compose (las versiones recientes exponen el tipo de contenido como semántica)
OutlinedTextField(
value = correo, onValueChange = { correo = it },
label = { Text("Correo electrónico") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Next),
modifier = Modifier.semantics { contentType = ContentType.EmailAddress }
)
// React Native
<TextInput autoComplete="email" textContentType="emailAddress" keyboardType="email-address" />
// Flutter
TextField(autofillHints: const [AutofillHints.email], keyboardType: TextInputType.emailAddress) Usa también imeAction o returnKeyType («Siguiente», «Hecho») para que la tecla de retorno avance al siguiente campo en el orden lógico, y desactiva la corrección automática en campos donde estorba (nombres, NIF, IBAN).
Errores: identificar, describir y llevar hasta ellos
Los criterios son los mismos que en web: 3.3.1 Identificación de errores, 3.3.3 Sugerencias ante errores y, para los mensajes que aparecen sin mover el foco, 4.1.3 Mensajes de estado. Lo que cambia es la API:
- Compose:
OutlinedTextField(isError = true, supportingText = { Text("Introduce un NIF con 8 números y una letra") })yModifier.semantics { error("NIF no válido") }para que TalkBack anuncie el error al llegar al campo. - Views:
textInputLayout.error = "Introduce un NIF con 8 números y una letra"; el componente muestra el texto y lo expone al servicio de accesibilidad. - iOS: el mensaje de error es un texto visible junto al campo; inclúyelo en el
accessibilityValueo elaccessibilityHintdel campo y, al enviar, anuncia el número de errores y lleva el foco al primero (lo viste en el ejemplo de la lección de iOS).
Valida al enviar o al salir del campo, no en cada pulsación: un mensaje de error que salta mientras alguien escribe con el lector de pantalla interrumpe constantemente la lectura. Y nunca indiques el error solo con un borde rojo (1.4.1 Uso del color) ni solo con una vibración: la vibración es una buena señal adicional, nunca la única (1.3.3 Características sensoriales).
Ejemplo
App de cita previa: al pulsar «Pedir cita» con el teléfono incompleto, la pantalla se desplaza hasta el campo, muestra debajo «El teléfono debe tener 9 cifras; has escrito 8», el campo expone el error a TalkBack y VoiceOver, y un anuncio indica «Hay 1 error en el formulario». Los datos ya escritos se conservan: nada obliga a empezar de nuevo.
Entrada redundante y procesos de varios pasos
3.3.7 Entrada redundante (A) pide que, dentro de un mismo proceso, no se exija volver a escribir información ya aportada: se rellena automáticamente o se ofrece para seleccionarla. En una app de envíos, la dirección de facturación ofrece «Usar la dirección de entrega»; en un trámite de varios pasos, el NIF del paso 1 no se vuelve a pedir en el paso 4. Las excepciones (volver a escribir una contraseña por seguridad, por ejemplo) son limitadas.
En procesos de varios pasos, anuncia el paso actual con el título de pantalla («Paso 2 de 4: datos de contacto»), conserva los datos al retroceder y permite revisar antes de confirmar operaciones con efectos económicos o legales (en esa línea va el criterio 3.3.4 Prevención de errores (legales, financieros, de datos)).
Autenticación accesible
El criterio 3.3.8 Autenticación accesible (mínimo) (AA) prohíbe que un paso de inicio de sesión dependa de una prueba de función cognitiva (recordar una contraseña, transcribir un código, resolver un puzle) salvo que exista una alternativa o un mecanismo de ayuda. En una app, eso se traduce en:
- Permitir gestores de contraseñas y pegar: marca los campos con
.usernamey.password(o.newPassworden el alta) en iOS,autofillHintsusernameypassworden Android. No bloquees el pegado en ningún campo de credenciales. - Ofrecer biometría o llaves de acceso: Face ID, Touch ID o huella (
BiometricPrompt) y passkeys (llaves de acceso, que iOS y Android integran en el sistema y Android gestiona con Credential Manager) evitan memorizar. Pero la biometría no puede ser la única vía: hay personas que no pueden usarla. - Códigos de un solo uso autocompletables:
textContentType = .oneTimeCodeen iOS sugiere el código recibido por SMS sobre el teclado; en Android, la pistasmsOTPCodede autocompletado o las API de SMS Retriever y SMS User Consent permiten rellenarlo sin transcribir. - Tiempo suficiente: si el código caduca, avisa y permite pedir otro sin perder lo hecho (2.2.1 Tiempo ajustable).
- Sin CAPTCHA cognitivo: si hace falta protección antiabuso, usa mecanismos que no exijan reconocer imágenes ni descifrar caracteres (3.3.8 admite como excepción el reconocimiento de objetos, pero 3.3.9, de nivel AAA, no).
Error frecuente
El código de verificación se pide en seis cajas de una cifra, cada una un campo independiente. El autocompletado del sistema no funciona, pegar el código solo rellena la primera caja y VoiceOver anuncia seis campos sin etiqueta. La alternativa correcta es un único campo con .oneTimeCode o smsOTPCode, etiqueta «Código de 6 cifras» y, si se quiere la apariencia de cajas, dibujarla sobre ese único campo.
Selectores, fechas y campos especiales
- Prefiere los selectores nativos (
UIDatePicker,DatePickerde Material 3): son accesibles y además permiten escribir la fecha. Un calendario a medida exige el trabajo de la lección de Android: cada día con nombre completo, estado y rol. - Los campos de contraseña con botón «Mostrar» necesitan nombre y estado en ese botón («Mostrar contraseña», desactivado).
- Las casillas de condiciones y privacidad deben ser controles reales (
Toggle,CheckboxoModifier.toggleable), con el texto asociado como etiqueta y un objetivo táctil de 48 dp o 44 pt que incluya ese texto. - Comprueba que el teclado en pantalla no tapa el campo activo ni el botón de enviar (2.4.11 Foco no oculto (mínimo)); en Android, ajusta el comportamiento con
windowSoftInputModeo los insets del teclado en Compose.
Pruébalo tú
Abre la pantalla de inicio de sesión de una app que uses. Comprueba si el gestor de contraseñas del móvil te ofrece tus credenciales, si puedes pegar la contraseña y si el código del SMS aparece sugerido sobre el teclado. Cualquier «no» es un indicio de incumplimiento de 3.3.8 o de 1.3.5 que merece revisarse.
Resumen
- Etiquetas visibles y persistentes, nunca solo un placeholder; el nombre accesible contiene el texto visible (2.5.3).
- Teclado adecuado y propósito de la entrada expuesto con
textContentType(iOS),autofillHints(Android y Flutter),contentType(Compose) oautoComplete(React Native) (1.3.5). - Errores en texto, asociados al campo, anunciados y con sugerencia de corrección (3.3.1, 3.3.3, 4.1.3); nunca solo color o vibración.
- No pidas dos veces lo mismo en un proceso (3.3.7) y conserva los datos al retroceder.
- Autenticación sin pruebas de memoria (3.3.8): gestores de contraseñas, pegado, biometría con alternativa, llaves de acceso y códigos autocompletables en un único campo.
Comprueba lo que has aprendido
Esta lección se puede leer libremente. Para hacer su test de 3 preguntas, los ejercicios prácticos con páginas reales, guardar tu avance y optar al examen y al certificado, matricúlate en el curso.
Matricularme · nivel 6 (119 €)