Planteamiento y contexto
Un sistema de diseño necesita un tratamiento de foco por teclado unificado para botones, campos de entrada, menús y controles personalizados. La implementación actual solo cambia el color del borde, desaparece en un tema de bajo contraste y modifica el tamaño del componente cuando recibe el foco. Diseñe un enfoque reutilizable y una puerta de control de pruebas.
WCAG 2.2 agrega Focus Appearance como un criterio de conformidad sobre el área del indicador y el contraste con los colores adyacentes. La entrevista evalúa si el candidato puede convertir ese requisito en estados de componentes, tokens de CSS, rutas de teclado y verificaciones medibles en lugar de simplemente añadir un contorno.
Qué evalúa el entrevistador
Se busca evaluar la distinción entre el indicador y el componente, los cálculos de área y contraste, un tratamiento que no se base únicamente en el color, controles no rectangulares y redondeados, ausencia de desplazamiento de diseño (layout shift), semántica nativa y cobertura de :focus-visible, entrada por puntero y modo de colores forzados.
Preguntas para clarificar
- ¿El objetivo es WCAG 2.2 AA o AAA, y qué rutas son operables por teclado?
- ¿El anillo está en el exterior, en el interior o desplazado respecto al componente?
- ¿Qué tokens de tema y reglas de colores forzados existen?
- ¿Los controles personalizados utilizan elementos nativos o roles y estados correctos?
- ¿Cómo se componen los estados de foco, hover, seleccionado e inválido?
- ¿Cómo se probarán los controles redondeados, el desplazamiento (scrolling) y los fondos con imágenes?
Respuesta en 30 segundos
“Haría un inventario de los elementos operables, utilizaría controles nativos donde sea posible y proporcionaría un indicador :focus-visible que no se base solo en el color. Usaría un outline o pseudo-elemento que no altere el diseño, con tokens para la geometría del anillo y el contraste tanto frente al componente como frente al fondo adyacente. Preservaría los colores del sistema en modo de colores forzados. Validaría rutas de teclado completas, píxeles renderizados, zoom y verificaciones asistivas manuales.”
Solución paso a paso
Paso 1: Definir los objetivos y estados de foco
Priorice botones, inputs, selects y enlaces nativos. Un div personalizado requiere manejo de teclado, rol, nombre, valor y sincronización de estado. focus-visible ayuda a distinguir entre el foco de puntero y de teclado, pero no puede eliminar la respuesta visual del teclado.
Defina la prioridad para los estados default, hover, focus-visible, selected, invalid y disabled de modo que el anillo siga siendo identificable cuando se apliquen estilos de error o selección.
Paso 2: Dibujar sin alterar el diseño (layout)
No aumente el ancho del borde al recibir el foco; esto cambia la caja del elemento y desplaza a los elementos vecinos. Prefiera outline, outline-offset o un pseudo-elemento. Para formas complejas, combine sombras interiores y exteriores asegurándose de que no queden recortadas.
.control:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 2px;
}Evalúe el perímetro renderizado de botones redondeados, botones de iconos y controles deslizantes (sliders) en lugar de una línea rectangular recortada por un contenedor padre.
Paso 3: Convertir WCAG 2.4.13 en tokens y umbrales
Centralice los tokens de ancho de anillo, desplazamiento (offset), color de foco, fondo y estado de error. Pruebe el contraste frente al componente sin foco y frente al fondo adyacente, no solo frente al texto. Tome muestras del peor color local cuando existan degradados, imágenes o capas translúcidas.
focus-ring-width >= 2 CSS px
focus-ring-contrast-against-adjacent >= required threshold
focus-ring-area >= minimum perimeter-area ruleUtilice la geometría y los píxeles renderizados para validar el área y el contraste; los colores en los archivos de diseño no son prueba de cumplimiento en tiempo de ejecución.
Paso 4: Soportar temas, colores forzados y anulaciones del sistema
Asigne tokens de foco independientes para temas claros, oscuros y de marca. Bajo forced-colors: active, no oculte el indicador de la plataforma; los colores del sistema como ButtonText pueden preservar la visibilidad.
@media (forced-colors: active) {
.control:focus-visible {
outline: 2px solid ButtonText;
outline-offset: 2px;
}
}Nunca use outline: none a menos que el mismo estado proporcione un indicador visible igualmente claro.
Paso 5: Cubrir controles dinámicos y desplazamiento
Los menús, diálogos, comboboxes y listas virtuales deben probarse para verificar el movimiento del foco, el retorno del foco al cerrar y que los anillos permanezcan visibles al desplazarse. Las transformaciones, clip paths y overflow no deben ocultar el indicador.
Con aria-activedescendant, el indicador visual sigue al elemento activo aunque el foco del DOM permanezca en el contenedor, y las vistas de teclado y lector de pantalla deben coincidir.
Paso 6: Construir criterios de aceptación automatizados y manuales
Automatice comprobaciones para elementos enfocables, outline o shadow calculados, tokens de tema, reglas de colores forzados y capturas de pantalla a través de diferentes estados y fondos. Manualmente use Tab, Shift+Tab, Enter, Space, flechas y Escape; pruebe con 200% de zoom y ajustes de alto contraste con diferentes dispositivos de entrada.
Registre componente, estado, fondo, geometría del anillo, resultado de contraste, pasos de teclado y capturas de pantalla. Una línea azul visible en un archivo de diseño no es prueba de conformidad.
Respuesta modelo
“Hago un inventario de los controles operables y priorizo la semántica nativa. :focus-visible utiliza un contorno y desplazamiento neutrales para el diseño, mientras que los tokens mantienen consistentes el ancho y el contraste del anillo. El foco no debe agregar ancho de borde ni depender únicamente del color. Pruebo los modos claro, oscuro y de colores forzados, preservando la visibilidad del sistema.”
“Para menús, diálogos, listas virtuales y controles no rectangulares verifico el movimiento del foco, el retorno del foco, los recortes y la indicación de elementos activos. La automatización captura estilos calculados y píxeles; las comprobaciones manuales cubren rutas de teclado, zoom al 200% y alto contraste.”
Errores comunes
- Cambiar solo el color de texto o de borde → el anillo se funde con el fondo → pruebe el anillo y los colores adyacentes por separado.
- Añadir ancho de borde → el diseño se desplaza → use un contorno o un elemento neutral para el diseño.
- Forzar
outline: none→ los usuarios de teclado pierden retroalimentación → proporcione un indicador visible equivalente. - Probar solo en tema claro → los colores oscuros o forzados desaparecen → cubra cada modo de tema.
- Usar únicamente capturas rectangulares → se pasa por alto la geometría redondeada y recortada → pruebe formas renderizadas y desplazamiento.
- Poner el foco en un div → la semántica y la salida asistiva divergen → prefiera controles nativos y ARIA válido.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Es outline mejor que box-shadow?
No hay un ganador universal. Outline es neutral para el diseño y posee semántica de plataforma; las sombras pueden crear anillos en capas. Elija según el recorte, la geometría redondeada, el comportamiento con colores forzados y los resultados de píxeles renderizados.
Pregunta de seguimiento 2: ¿Por qué :focus-visible no puede reemplazar a :focus?
Es una heurística del navegador para determinar cuándo mostrar el foco. No debe eliminar la semántica de foco ni dejar las rutas de teclado y tecnologías asistivas sin retroalimentación.
Pregunta de seguimiento 3: ¿Un borde rojo de error reemplaza al anillo de foco?
No. El error comunica un problema; el anillo comunica el destino actual. Ambos deben permanecer visibles sin depender únicamente del color.
Pregunta de seguimiento 4: ¿Cómo se prueba un fondo con imagen o degradado?
Tome muestras del peor color local adyacente al anillo renderizado, agregando una base opaca o un segundo anillo cuando sea necesario. Un color promedio del diseño no es prueba suficiente.
Pregunta de seguimiento 5: ¿Cómo se evita que el overflow recorte el anillo?
Inspeccione el overflow, clip paths y transforms de los contenedores ascendentes; use anillos interiores, padding o un pseudo-elemento a nivel de componente para ajustar la geometría en lugar de deshabilitar el estilo de foco.