Publicado: 5 de marzo de 2026
El atributo focusgroup de HTML es una forma declarativa propuesta para agregar navegación con las teclas de flecha del teclado a widgets compuestos, como barras de herramientas, listas de pestañas, menús, listas desplegables, etcétera, sin escribir ningún código JavaScript de roving-tabindex. Un atributo reemplaza cientos de líneas de código estándar. Queremos conocer tu opinión antes de que se lance esta función.
Pruébala y envíanos tus comentarios
Puedes probar focusgroup hoy mismo en Chrome, Edge y otros navegadores basados en Chromium. Para ello, habilítalo de una de las siguientes dos maneras:
- Pruebas locales: En el navegador, abre la página
about://flagsy habilita la marca Experimental Web Platform features. O bien, inicia el navegador desde la línea de comandos con el parámetro--enable-blink-features=Focusgroup. - Prueba de origen: Regístrate en la prueba de origen de focusgroup para probarla en tu sitio con usuarios reales.
Luego, explora las demostraciones interactivas para ver cada patrón en acción.
Necesitamos tu opinión. Presenta un problema de grupo de enfoque para contarnos lo que piensas.
Este es un esfuerzo entre navegadores: La propuesta se originó en Microsoft a través del OpenUI Community Group con el fuerte apoyo de Google. La forma de la API puede cambiar según tus comentarios. Analicemos el problema que resuelve el grupo de enfoque y cómo funciona la API.
El problema: tabindex itinerante manual
Si alguna vez creaste una barra de herramientas, una lista de pestañas, un menú o un listbox, escribiste alguna versión de este código. La Guía de prácticas de creación de ARIA (APG) recomienda que los widgets compuestos presenten una sola parada de tabulación y permitan que los usuarios se muevan entre los elementos con las teclas de flecha. Este patrón se conoce como "tabindex itinerante". Muchos frameworks de IU reimplementan esto desde cero:
<div role="toolbar" aria-label="Text formatting" id="toolbar">
<button type="button" tabindex="0">Bold</button>
<button type="button" tabindex="-1">Italic</button>
<button type="button" tabindex="-1">Underline</button>
<button type="button" tabindex="-1">Strikethrough</button>
</div>
A partir de aquí, los desarrolladores deben usar JavaScript que detecte las teclas de flecha para mover el enfoque y ajustar el atributo tabindex de todos los elementos. Esta es la versión simplificada. Una implementación de producción también debe controlar lo siguiente:
- Modo de escritura y RTL: Ajusta las direcciones de las teclas de flecha según la dirección del contenido.
- Memoria del último enfoque: Restablece el enfoque en el elemento activo anteriormente cuando un usuario vuelve a presionar Tab.
- Elementos inhabilitados y ocultos: Se omiten durante la navegación.
- Elementos dinámicos: Actualiza el índice itinerante cuando se agregan o quitan elementos.
La mayoría de las bibliotecas de IU, incluidas React, Angular CDK y Fluent UI, incluyen su propia versión de esta lógica. Esto implica mucho esfuerzo duplicado para obtener algo que podría ser un elemento primitivo de la plataforma.
La solución: el atributo focusgroup
Con focusgroup, la misma barra de herramientas se convierte en la siguiente:
<div focusgroup="toolbar" aria-label="Text formatting">
<button type="button">Bold</button>
<button type="button">Italic</button>
<button type="button">Underline</button>
<button type="button">Strikethrough</button>
</div>
Pruébalo en vivo: Toolbar Pattern > Basic Toolbar. Eso es todo. No hay JavaScript para la navegación con las teclas de flecha. No se requiere administración manual de tabindex. Esto es lo que ahora controla el navegador por ti:
- Navegación con teclas de flecha: Navega entre los elementos respetando el modo de escritura y la direccionalidad.
- Una sola parada de tabulación: El navegador contrae automáticamente los elementos participantes en una sola parada de tabulación. Los desarrolladores no necesitan establecer
tabindex="-1"en los elementos inactivos. - Memoria del último enfoque: Cuando un usuario sale del grupo enfocado y regresa, el enfoque se restablece en el elemento que dejó.
- Semántica de ARIA: El navegador proporciona roles adecuados (como
role="toolbar") según el comportamiento elegido cuando se usan elementos genéricos.
Los desarrolladores solo conservan la lógica exclusiva de sus funciones, como alternar el estado presionado, abrir menús, administrar la selección o cualquier comando personalizado.
Descripción general de la API
El atributo focusgroup toma una lista de tokens separados por espacios. El primer token siempre es un token de comportamiento que declara el patrón del widget. A continuación, se incluyen los tokens de modificador opcionales: focusgroup="<behavior> [inline|block] [wrap] [nomemory]".
Tokens de comportamiento
El token de comportamiento es obligatorio (a menos que se use none para inhabilitar un grupo de enfoque superior). Declara el patrón de widget compuesto, lo que garantiza que se puedan inferir los roles correctos cuando no se especifiquen de otro modo. Los tokens siguen los patrones que se describen en la guía de prácticas de creación de Aria y se enumeran en la siguiente tabla:
| Comportamiento | Patrón de APG | Rol mínimo del contenedor (cuando se aplica) | Rol secundario mínimo (cuando se aplica) |
Modificadores predeterminados |
|---|---|---|---|---|
toolbar |
Barra de herramientas | barra de herramientas | (ninguno) | inline |
tablist |
Pestañas de APG | tablist | tab | inline wrap |
radiogroup |
Grupo de botones de selección | radiogroup | radio | (ninguno) |
listbox |
Listbox | listbox | option | (ninguno) |
menu |
Menú | menú | menuitem | block wrap |
menubar |
Barra de menú | menubar | menuitem | inline wrap |
none |
N/A | N/A | N/A | N/A |
Consulta la explicación para obtener todos los detalles sobre cómo funciona la asignación de roles.
Restricción del eje (inline y block)
Si el comportamiento elegido no tiene modificadores predeterminados, las cuatro teclas de flecha funcionan para mover el enfoque. Puedes restringir la navegación a un solo eje lógico con los modificadores inline o block:
inline: El grupo de enfoque solo responde a las teclas de flecha en el eje intercalado, a la izquierda y a la derecha en la mayoría de los contextos en inglés (horizontal, de arriba hacia abajo).block: El grupo de enfoque solo responde a las teclas de flecha en el eje de bloque, hacia arriba y hacia abajo en la mayoría de los contextos en inglés (horizontal, de arriba hacia abajo).
La restricción de ejes se alinea con las propiedades lógicas de CSS y se adapta automáticamente al modo y la dirección de escritura.
Navegación envolvente
De forma predeterminada, la navegación con las teclas de flecha se detiene en los bordes del focusgroup. Agrega el modificador wrap para repetir el bucle desde el último elemento hasta el primero (y desde el primero hasta el último). Si un comportamiento tiene ajuste de forma predeterminada, usa el modificador nowrap para inhabilitarlo.
Pruébalo en vivo: Patrón de Tablist > Tablist horizontal con ajuste de texto. En ese ejemplo, cuando el enfoque está en la pestaña Preguntas frecuentes y el usuario presiona la tecla de flecha hacia la derecha, el enfoque vuelve a la pestaña Descripción general.
El atributo focusgroupstart
El atributo focusgroupstart marca qué elemento recibe el enfoque cuando se presiona Tab por primera vez en un grupo de enfoque (o cada vez que se inhabilita la memoria):
<div focusgroup="toolbar nomemory" aria-label="Entry point demo">
<button type="button">First</button>
<button type="button" focusgroupstart>Middle (Entry)</button>
<button type="button">Last</button>
</div>
Las combinaciones Tab y Mayúsculas+Tab se detienen en "Middle (Entry)" porque tiene focusgroupstart y la memoria está inhabilitada con el modificador nomemory. Pruébalo en vivo:
Toolbar Pattern > Entry Point with focusgroupstart.
Inhabilita la memoria (nomemory)
De forma predeterminada, los focusgroups recuerdan el último elemento enfocado y lo restablecen cuando se vuelve a ingresar con la tecla Tab. Para los patrones en los que el enfoque siempre debe volver a un punto de entrada fijo (como en la demostración anterior), usa el modificador nomemory en el atributo focusgroup para inhabilitarlo.
Este modificador también se puede combinar con el movimiento programático de focusgroupstart para brindarte un control total sobre el elemento que se enfoca cuando se ingresa al grupo. La memoria se borra cuando el elemento recordado deja de estar disponible, por ejemplo, si se quita, se oculta, se inhabilita, se vuelve inerte o se excluye del grupo de enfoque.
Inhabilitar (focusgroup="none")
Usa focusgroup="none" para excluir un elemento y su subárbol de la navegación con flechas de un grupo de enfoque superior. El elemento inhabilitado y su subárbol siguen siendo accesibles con la tecla Tab, pero las teclas de flecha los omiten:
<div focusgroup="toolbar" aria-label="Segmented toolbar">
<button type="button">New</button>
<button type="button">Open</button>
<button type="button">Save</button>
<span focusgroup="none">
<button type="button">Help</button>
<button type="button">Shortcuts</button>
</span>
<button type="button">Close</button>
<button type="button">Exit</button>
</div>
Con la tecla de flecha hacia la derecha, se navega a Nuevo, luego a Abrir, Guardar, Cerrar y Salir, y se omiten por completo los botones Ayuda y Atajos. Sin embargo, el usuario puede presionar la tecla Tab para acceder a estos botones. Pruébalo en vivo: Additional Concepts > Opt-Out Segments with focusgroup="none".
Patrones comunes
Tablist
Es un control de pestañas con navegación por teclas de flecha entre pestañas.
<div focusgroup="tablist nomemory" aria-label="Sections">
<button type="button" aria-selected="true" aria-controls="panel-overview" id="tab-overview" focusgroupstart>Overview</button>
<button type="button" aria-selected="false" aria-controls="panel-features" id="tab-features">Features</button>
<button type="button" aria-selected="false" aria-controls="panel-pricing" id="tab-pricing">Pricing</button>
<button type="button" aria-selected="false" aria-controls="panel-faq" id="tab-faq">FAQ</button>
</div>
<div role="tabpanel" id="panel-overview" aria-labelledby="tab-overview" tabindex="0">...</div>
<div role="tabpanel" id="panel-features" aria-labelledby="tab-features" tabindex="0">...</div>
<div role="tabpanel" id="panel-pricing" aria-labelledby="tab-pricing" tabindex="0">...</div>
<div role="tabpanel" id="panel-faq" aria-labelledby="tab-faq" tabindex="0">...</div>
Pruébalo en vivo: Patrón de Tablist > Tablist horizontal con ajuste de texto.
Qué debes tener en cuenta:
- El atributo
focusgroupstartse encuentra en la pestaña seleccionada, por lo que el enfoque siempre ingresa allí. - El modificador
nomemorygarantiza que, incluso si el usuario se había enfocado previamente en otra pestaña, el reingreso siempre vaya a la pestaña seleccionada. - El modificador
inlinerestringe la navegación con flechas solo a las teclas de izquierda y derecha. Esto coincide con el comportamiento esperado que se describe en el patrón de pestañas de APG. - El modificador
wrappermite a los usuarios usar las teclas de flecha de forma continua en todas las pestañas. - El código del desarrollador, que se omitió para mayor brevedad, controla la selección real: actualiza
aria-selected, alterna la visibilidad del panel y mueve el atributofocusgroupstartcuando cambia la selección.
Menú y barra de menú
Un menú vertical simple con navegación de flechas hacia arriba y hacia abajo.
<div focusgroup="menu" aria-label="File actions" class="menu-vertical">
<button type="button" class="menu-item">New</button>
<button type="button" class="menu-item">Open…</button>
<button type="button" class="menu-item">Save</button>
<button type="button" class="menu-item">Exit</button>
</div>
Pruébalo en vivo: Patrón de menú y barra de menú > Menú vertical simple.
Con el modificador block, solo las teclas de flecha hacia arriba y hacia abajo permiten navegar por los elementos. Las teclas de flecha hacia la izquierda y la derecha están disponibles para el comportamiento que definas (por ejemplo, abrir submenús). En el caso de una barra de menú con submenús anidados, cada nivel es un focusgroup independiente. Probar en vivo:
Patrón de menú y barra de menú > Barra de menú con submenús emergentes
<ul role="menubar" focusgroup="menubar"
aria-label="Application Menu" class="menubar">
<li role="none">
<button role="menuitem" type="button" class="menubar-item"
aria-haspopup="menu" aria-expanded="false"
popovertarget="filemenu">File</button>
<ul role="menu" focusgroup="menu"
id="filemenu" popover aria-label="File submenu" class="submenu">
<li role="none"><button type="button" class="submenu-item"
autofocus>New</button></li>
<li role="none"><button type="button" class="submenu-item">Open</button></li>
<li role="none"><button type="button" class="submenu-item">Save</button></li>
</ul>
</li>
<!-- More menu items... -->
</ul>
Pruébalo en vivo: Patrón de menú y barra de menú > Barra de menú con submenús emergentes.
Si bien la barra de menú usa el modificador inline para la navegación hacia la izquierda y la derecha, los submenús usan el modificador block para la navegación hacia arriba y hacia abajo. Los grupos de enfoque anidados son completamente independientes, por lo que no interfieren entre sí.
Radiogroup
Un grupo de botones de selección personalizado con navegación por teclas de flecha y control total del diseño.
<div focusgroup="radiogroup" aria-label="Favorite color">
<span aria-checked="false" tabindex="0">Red</span>
<span aria-checked="false" tabindex="0">Green</span>
<span aria-checked="true" tabindex="0" focusgroupstart >Blue</span>
<span aria-checked="false" tabindex="0">Purple</span>
</div>
Pruébalo en vivo: Patrón de grupo de botones de selección > Comparación: nativo frente a Focusgroup.
Si bien el atributo focusgroup controla la navegación con las teclas de flecha, debes implementar el código de selección. En esta demostración, el código JavaScript administra el estado de verificación (con el atributo aria-checked).
Conceptos clave
Participación en elementos de grupos focales
Todos los descendientes enfocables de forma secuencial del elemento con focusgroup establecido en un comportamiento válido se consideran participantes de ese grupo de enfoque. Esto significa que no se consideran los elementos con un tabindex negativo, pero sí los elementos enfocables de forma nativa, como <button>, así como los elementos en los que especificaste un tabindex no negativo.
Detención de tabulación
No es necesario que administres los valores de tabindex. Incluso cuando varios elementos secundarios son naturalmente navegables con la tecla Tab (por ejemplo, varios elementos <button>), focusgroup los contrae en una sola parada de tabulación. El navegador controla qué elemento se puede enfocar con la tecla Tab en cualquier momento. Pruébalo en vivo:
Toolbar Pattern > No tabindex Management Needed.
Memoria del último enfoque
De forma predeterminada, cuando un usuario presiona Tab para salir de un focusgroup y luego vuelve a presionar Tab, el enfoque regresa al último elemento enfocado. Esto es fundamental para las listas y barras de herramientas grandes, de modo que los usuarios no pierdan la posición. Usa el modificador nomemory para inhabilitar este comportamiento cuando quieras que el enfoque siempre se restablezca en el primer elemento o, si usas focusgroupstart, para controlar el elemento enfocado inicialmente.
Grupos focales anidados
Cada declaración de focusgroup crea un alcance independiente. Un grupo de enfoque anidado inhabilita automáticamente la navegación con flechas de su elemento superior. Usa la tecla Tab para moverte entre los grupos de enfoque y las teclas de flecha para navegar dentro del grupo de enfoque actual. Pruébalo en vivo: Additional Concepts > Nested Focusgroups.
Compatibilidad con Shadow DOM
Focusgroup se aplica a través de los límites del DOM secundario de forma predeterminada. Un focusgroup declarado en un host de sombra incluye elementos enfocables dentro del árbol de sombra de ese host. Si quieres inhabilitar la opción, puedes usar focusgroup="none" dentro del árbol de sombra de tu componente.
Manejo de conflictos de claves
Algunos elementos dentro de un focusgroup, como <input>, <textarea> y otros controles, usan las teclas de flecha para sus propios fines. Cuando hay un conflicto entre las teclas de navegación del grupo de enfoque y el comportamiento de las teclas de flecha de un elemento nativo, sucede lo siguiente:
- El elemento interactivo consume las teclas de flecha (por ejemplo, para el movimiento del cursor de texto), y focusgroup no interfiere.
- Tab o Mayúsculas + Tab proporcionan un mecanismo de escape predeterminado que permite al usuario usar la navegación con Tab para "volver a ingresar" al grupo de enfoque.
Estos comportamientos de escape solo se aplican cuando hay un conflicto de teclas real; los ejes que no están en conflicto no se ven afectados. También puedes llamar a preventDefault() en eventos keydown para anular el comportamiento de las teclas de flecha del grupo de enfoque para elementos específicos. Esto significa que puedes incluir entradas y áreas de texto dentro de un focusgroup sin interrumpir ninguno de los dos comportamientos.
Si agregas controladores de teclas a tus propios elementos que participan en un grupo de enfoque, asegúrate de proporcionar un mecanismo de escape similar para que los usuarios puedan acceder al resto del grupo.
Descubrimiento de descendientes profundos
Los elementos de Focusgroup no necesitan ser elementos secundarios directos del contenedor de Focusgroup.
El navegador considera que todos los descendientes enfocables de forma secuencial (tabindex no negativo) participan en el grupo de enfoque, a menos que estén dentro de un grupo de enfoque anidado o que hayan inhabilitado la participación con focusgroup="none".
<div focusgroup="toolbar" aria-label="Nested wrappers">
<div>
<span>
<button type="button">Alpha</button>
</span>
<span>
<button type="button">Beta</button>
</span>
<span>
<button type="button">Gamma</button>
</span>
</div>
</div>
La navegación con las teclas de flecha funciona incluso si los botones están anidados dentro de los contenedores <div> y <span>. No hay ningún requisito de lista plana, por lo que los elementos de wrapper para el diseño están bien.
Pruébalo en vivo: Additional Concepts > Deep Descendants.
Integración con la propiedad reading-flow
La navegación secuencial (Tab) y direccional (teclas de flecha) respetan la propiedad reading-flow de CSS cuando está presente, siguiendo el orden de lectura visual en lugar del orden de origen del DOM.
Esto garantiza que la navegación con las teclas de flecha coincida con el diseño que los usuarios ven en la pantalla.
<div focusgroup="toolbar" aria-label="Visual order"
style="display: flex; flex-direction: row-reverse; reading-flow: flex-visual;">
<button type="button">A (DOM first)</button>
<button type="button">B (DOM second)</button>
<button type="button">C (DOM third)</button>
</div>
Si bien el orden del DOM es A, B y C, el orden visual es C, B y A porque el diseño usa flex-direction: row-reverse. Sin embargo, como el código también usa reading-flow: flex-visual, el orden de lectura vuelve a ser A, B, C, y focusgroup coincide con este orden.
Si presionas Tab, primero se enfocará C, y si presionas la flecha hacia la derecha, se enfocará B y, luego, A. Pruébalo en vivo: Conceptos adicionales > Integración del flujo de lectura de CSS.
Accesibilidad
Inferencia de roles de ARIA
En un grupo de enfoque, el token de comportamiento se usa para inferir un rol mínimo tanto para el contenedor como para sus elementos participantes. Esto significa que, cuando el atributo focusgroup se establece en un elemento que tiene un rol genérico, se aplica el rol correcto según el comportamiento elegido. Los elementos participantes del elemento que tienen un rol genérico o los botones que no tienen un rol que especificaste tendrán sus roles inferidos de manera correspondiente. Por ejemplo, el siguiente código HTML:
<div focusgroup="tablist">
<button>Tab 1</button>
<button>Tab 2</button>
<button>Tab 3</button>
</div>
Crea el siguiente árbol de accesibilidad, aunque no se hayan definido roles en los botones:
+ tablist
|
+ tab
|
+ tab
|
+ tab
Siempre puedes controlar el comportamiento configurando el rol directamente.
Consideraciones de accesibilidad
Ten cuidado de respetar el comportamiento que elegiste cuando creaste un grupo de enfoque.
El uso de Focusgroup debe alinearse lo más posible con el comportamiento que especificaste. Esto es importante para garantizar que los usuarios que dependen de las herramientas de accesibilidad puedan navegar por el contenido y usar controles personalizados.
Si bien la inferencia de roles proporciona buenos valores predeterminados, cuando se usan elementos con roles no genéricos, se debe tener cuidado para garantizar que tengan el rol adecuado establecido para la funcionalidad que proporcionan.
Cuando uses focusgroup, recuerda que es posible que los usuarios necesiten desplazarse con las teclas de flecha para ver tu contenido. Siempre debe haber una forma para que un usuario de teclado pueda leer y acceder al contenido de tu página.
Detección de características
Para comenzar a usar focusgroup hoy mismo, antes de que sea totalmente compatible con todos los navegadores, puedes detectar la compatibilidad con focusgroup en JavaScript:
if ('focusgroup' in HTMLElement.prototype) {
// focusgroup is supported.
} else {
// fall back to manual roving tabindex.
}
Conclusión
El atributo focusgroup está avanzando en los organismos de estándares, y estamos creando activamente el prototipo en Chromium y perfeccionando la API.
Pruébalo y registra un problema del grupo de enfoque en la herramienta de seguimiento de problemas de GitHub de Open-UI. En especial, nos interesan tus opiniones sobre lo siguiente:
- ¿La superficie de la API se siente adecuada para los patrones que compilas?
- ¿Hay patrones o situaciones que no estamos teniendo en cuenta?
- ¿Hay elementos en los que no se debería permitir el atributo focusgroup?
- ¿Cómo funciona la historia de accesibilidad para tus casos de uso?
Gracias por ayudarnos a mejorar la navegación con el teclado en la Web.
Más información
- Explicación del grupo de enfoque
- Demostraciones interactivas (Fuente)
- Problema de HTML de WHATWG
- Problemas del grupo de enfoque de la IU abierta
- Guía de prácticas de creación de ARIA
Gracias a Mason Freed, Sara Higley, Scott O'Hara y el resto de la comunidad de Open-UI por su ayuda para recuperar focusgroup.