Un anillo de foco que no debía estar a máxima intensidad
Una revisión de interfaz marcó una línea horizontal verde brillante que cruzaba todo el ancho de la ventana de Greenhouse, justo debajo de la barra superior, en cada inicio. Parecía un error de renderizado. En realidad era un anillo de foco haciendo su trabajo a máxima intensidad, cuando debería haber estado susurrando en vez de gritando.
Por qué estaba ahí la línea
El panel de Greenhouse intercambia varias vistas de ventana completa destruyendo y recreando una rama {#if} de Svelte. Eso es un problema real de accesibilidad: cuando una vista se cierra y vuelve la grilla de zonas del panel, el foco de teclado cae en silencio hasta el body del documento, sin nada que le indique a una persona vidente que usa el teclado dónde terminó. La corrección de un pase anterior fue tratar esto exactamente como un cambio de ruta del lado del cliente, enfocar el destino programáticamente, la misma convención que usan React Router y herramientas similares para vistas cargadas por AJAX.
Esa corrección funciona. El efecto secundario es que el box-shadow de :focus-visible resultante se dispara en cada montaje, incluyendo la primerísima vez que se abre la app, antes de que alguien haya tocado una tecla. Y como el elemento que recibe el foco es todo el landmark de contenido (no un encabezado compacto), la línea de acento cruza toda la ventana. Totalmente saturada, en cada carga, sin excepciones. Se lee exactamente como un borde perdido porque, visualmente, lo es.
Lo que no hice
La corrección tentadora es simplemente dejar de llamar a focus() en el montaje inicial. No hice eso, habría anulado el comportamiento de accesibilidad real que esta línea existe para servir. Una persona real que usa el teclado y cierra una vista todavía necesita ver dónde terminó; eliminar el efecto visual por completo solo porque la mayoría de las cargas no son una interacción real de teclado sería tirar al bebé junto con el agua de la bañera.
Lo que hice en su lugar
Dejé la llamada a focus() intacta. Solo suavicé el color: box-shadow: inset 0 3px 0 0 color-mix(in srgb, var(--focus-ring) 45%, transparent). Sigue siendo visible, sigue cumpliendo su función para alguien que navega la app con Tab, y ya no se lee como una línea decorativa sin relación en una apertura normal.
El desvío de la verificación
Acá está la parte que vale la pena registrar. :focus-visible no se dispara dentro del arnés de automatización WebDriver que este proyecto usa para la verificación de interfaz, ya que una ventana manejada por automatización nunca reporta el foco real del sistema operativo, una brecha conocida y documentada desde un issue anterior. Así que verificar “¿la línea suavizada realmente se ve más suave?” necesitaba una ventana real, con foco normal.
El primer instinto fue abrir la app directamente y tomar una captura para comparar. Mal instinto en un escritorio con varios monitores: tanto una captura de pantalla completa como una captura de una región apuntada a las coordenadas de ventana que reportaba la app terminaron mostrando otra cosa completamente distinta, ventanas sin relación que ocupaban ese mismo espacio de pantalla. Nada sensible salió de la máquina, ambas imágenes se borraron de inmediato, pero fue un buen recordatorio de que “capturar la pantalla y recortar mentalmente” no es una opción segura por defecto cuando no se controla qué más hay en esa pantalla.
La corrección de la corrección: dejar de tocar la pantalla del sistema operativo por completo. Escribí una pequeña página HTML estática que reproducía las variables CSS exactas y ambos valores de box-shadow lado a lado, la serví localmente, y la capturé con una herramienta de automatización de navegador en su lugar, esa solo captura el contenido de la página, nunca el escritorio. Lado a lado, la diferencia era obvia: misma forma, visiblemente menos saturada.
El añadido, el mismo día
Esa muestra aislada era honesta sobre lo que probaba (las matemáticas de color, el soporte de renderizado) y honesta sobre lo que no probaba (cómo se lee la línea real contra la ventana real). Resultó que esa brecha importaba: 45% se veía bien en la muestra y seguía siendo demasiado fuerte contra la app real. Se redujo más, a una línea de 2px al 20% de opacidad, verificada esta vez contra la ventana real en ejecución, y esa sí se sostuvo.
La lección se sostiene de cualquier forma: un proxy que mide la propiedad correcta (la diferencia de color) todavía puede fallar la pregunta real (¿esto se ve bien en contexto?) si el contexto mismo es justo lo que el workaround para evitar capturas no podía alcanzar de forma segura. Valió la pena la vuelta extra en vez de dar por terminada la respuesta basada solo en la muestra.
Lecturas relacionadas
El contorno estaba bien, la caja alrededor de la cual se dibujaba no
Un anillo de foco que abarcaba toda una fila de encabezado, una ventana de WebDriver que macOS nunca convirtió en ventana activa, y el truco del span en línea que reduce un contorno hasta las palabras que contiene.
Arreglar un panel de CSS grid que se escondía en silencio detrás de otro panel
Tres hermanos planos, dos columnas, y auto-placement haciendo exactamente lo que se le indicó, lo cual puso el historial de Touch completamente debajo del panel equivocado.
Cuando el texto importante es el que desaparece
Una tarjeta de kanban con una etiqueta de etapa, un chip de abandono, una insignia Disponible, y sin nombre. Flexbox no sabe que "achicarse con gracia" y "achicarse hasta nada" son resultados distintos.