Publié le 5 mars 2026
L'attribut HTML focusgroup est une méthode déclarative proposée pour ajouter une navigation au clavier (à l'aide des touches fléchées) aux widgets composites tels que les barres d'outils, les listes d'onglets, les menus, les listes déroulantes, etc., sans écrire de code JavaScript roving-tabindex. Un seul attribut remplace des centaines de lignes de code récurrent. Nous aimerions connaître votre avis avant que cette fonctionnalité ne soit déployée.
Essayez-la et dites-nous ce que vous en pensez
Vous pouvez essayer focusgroup dès aujourd'hui dans Chrome, Edge et d'autres navigateurs Chromium en l'activant de deux manières :
- Tests locaux : dans le navigateur, ouvrez la page
about://flagset activez le flag Experimental Web Platform features (Fonctionnalités expérimentales de la plate-forme Web). Vous pouvez également lancer le navigateur à partir de la ligne de commande en utilisant le paramètre de ligne de commande--enable-blink-features=Focusgroup. - Version d'essai de l'origine : inscrivez-vous à la version d'essai de l'origine focusgroup pour la tester sur votre site avec de vrais utilisateurs.
Ensuite, explorez les démonstrations interactives pour voir chaque modèle en action.
Nous avons besoin de votre avis. Signalez un problème lié au groupe de discussion pour nous faire part de vos commentaires.
Il s'agit d'un effort inter-navigateur : la proposition provient de Microsoft via le groupe de la communauté OpenUI, avec le soutien de Google. La forme de l'API peut changer en fonction de vos commentaires. Découvrons le problème que résout focusgroup et le fonctionnement de l'API.
Le problème : index de tabulation manuels
Si vous avez déjà créé une barre d'outils, une liste d'onglets, un menu ou une zone de liste, vous avez déjà écrit une version de ce code. Le guide ARIA Authoring Practices (APG) recommande que les widgets composites présentent une seule tabulation et permettent aux utilisateurs de se déplacer entre les éléments à l'aide des touches fléchées. Ce modèle est connu sous le nom de "roving tabindex". De nombreux frameworks d'UI réimplémentent cela à partir de zéro :
<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>
À partir de là, les développeurs doivent utiliser JavaScript qui écoute les touches fléchées pour déplacer la sélection et ajuster l'attribut tabindex pour tous les éléments. Il s'agit de la version simplifiée. Une implémentation de production doit également gérer les éléments suivants :
- Mode écriture et RTL : ajustez le sens des touches fléchées en fonction du sens du contenu.
- Mémorisation du dernier élément sélectionné : le focus est rétabli sur l'élément précédemment actif lorsqu'un utilisateur revient à l'aide de la touche de tabulation.
- Éléments désactivés et masqués : ils sont ignorés pendant la navigation.
- Éléments dynamiques : mettez à jour l'index de déplacement lorsque des éléments sont ajoutés ou supprimés.
La plupart des bibliothèques d'UI, y compris React, Angular CDK et Fluent UI, fournissent leur propre version de cette logique. Cela représente beaucoup d'efforts redondants pour obtenir quelque chose qui pourrait être une primitive de plate-forme.
La solution : l'attribut focusgroup
Avec focusgroup, la même barre d'outils devient :
<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>
Essayez-le en direct : Modèle de barre d'outils > Barre d'outils de base. Et voilà ! Pas de JavaScript pour la navigation avec les touches fléchées. Aucune gestion manuelle de tabindex. Voici ce que le navigateur gère désormais pour vous :
- Navigation à l'aide des touches fléchées : naviguez entre les éléments en respectant le mode d'écriture et la directionnalité.
- Une seule tabulation : le navigateur réduit automatiquement les éléments participants en une seule tabulation. Les développeurs n'ont pas besoin de définir
tabindex="-1"sur les éléments non actifs. - Mémorisation du dernier élément sélectionné : lorsqu'un utilisateur quitte le groupe de focus et y revient, le focus est restauré sur l'élément qu'il a quitté.
- Sémantique ARIA : le navigateur fournit les rôles appropriés (comme
role="toolbar") en fonction du comportement choisi lorsque des éléments génériques sont utilisés.
Les développeurs ne conservent que la logique propre à leurs fonctionnalités, comme l'activation/la désactivation de l'état enfoncé, l'ouverture de menus, la gestion de la sélection ou toute commande personnalisée.
Présentation de l'API
L'attribut focusgroup accepte une liste de jetons séparés par un espace. Le premier jeton est toujours un jeton de comportement qui déclare le modèle de widget. Voici les jetons de modificateur facultatifs : focusgroup="<behavior> [inline|block] [wrap] [nomemory]".
Jetons de comportement
Le jeton de comportement est obligatoire (sauf si vous utilisez none pour désactiver un groupe de focus ancêtre). Il déclare le modèle de widget composite, ce qui permet de s'assurer que les bons rôles peuvent être déduits lorsqu'ils ne sont pas spécifiés. Les jetons suivent les modèles décrits dans le guide des pratiques de création Aria et sont listés dans le tableau suivant :
| Comportement | Format APG | Rôle minimal de conteneur (lorsqu'il est appliqué) | Rôle enfant minimal (lorsqu'il est appliqué) |
Modificateurs par défaut |
|---|---|---|---|---|
toolbar |
Barre d'outils | barre d'outils | (aucun) | inline |
tablist |
Onglets APG | tablist | tabulation | inline wrap |
radiogroup |
Groupe de boutons radio | radiogroup | radio | (aucun) |
listbox |
Listbox | listbox | option | (aucun) |
menu |
Menu | menu | menuitem | block wrap |
menubar |
Barre de menu | menubar | menuitem | inline wrap |
none |
n/a | n/a | n/a | n/a |
Pour en savoir plus sur le fonctionnement du mappage des rôles, consultez l'explication.
Restriction d'axe (inline et block)
Si le comportement choisi n'a pas de modificateurs par défaut, les quatre touches fléchées permettent de déplacer la sélection. Vous pouvez limiter la navigation à un seul axe logique en utilisant le modificateur inline ou block :
inline: le groupe de mise au point ne répond qu'aux touches fléchées sur l'axe intégré, à gauche et à droite dans la plupart des contextes en langue anglaise (horizontal, de haut en bas).block: le groupe de sélection ne répond qu'aux touches fléchées sur l'axe de bloc, vers le haut et vers le bas dans la plupart des contextes en langue anglaise (horizontalement, de haut en bas).
La restriction d'axe est alignée sur les propriétés logiques CSS et s'adapte automatiquement au mode et à la direction d'écriture.
Navigation enveloppante
Par défaut, la navigation par touches fléchées s'arrête aux limites du groupe de mise au point. Ajoutez le modificateur wrap pour boucler la liste du dernier élément au premier (et du premier au dernier). Si un comportement est défini sur "wrap" par défaut, utilisez le modificateur nowrap pour le désactiver.
Essayez-le en direct : Modèle de liste d'onglets > Liste d'onglets horizontale avec retour à la ligne. Dans cet exemple, lorsque le focus est sur l'onglet FAQ et que l'utilisateur appuie sur la touche flèche vers la droite, le focus revient à l'onglet Présentation.
Attribut focusgroupstart
L'attribut focusgroupstart indique l'élément qui reçoit le focus lorsque vous accédez à un groupe de focus pour la première fois (ou à chaque fois lorsque la mémoire est désactivée) :
<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>
Les touches Tabulation et Maj+Tabulation permettent d'accéder à "Middle (Entry)" (Milieu (entrée)) car il comporte focusgroupstart et que la mémoire est désactivée avec le modificateur nomemory. Essayez-le en direct :
Modèle de barre d'outils > Point d'entrée avec focusgroupstart.
Désactiver la mémoire (nomemory)
Par défaut, les groupes de sélection mémorisent le dernier élément sélectionné et le restaurent lors de la réentrée avec la touche Tab. Pour les modèles où la sélection doit toujours revenir à un point d'entrée fixe (comme dans la démo précédente), utilisez le modificateur nomemory dans l'attribut focusgroup pour le désactiver.
Ce modificateur peut également être combiné au mouvement programmatique de focusgroupstart pour vous donner un contrôle total sur l'élément sélectionné lors de l'entrée dans le groupe. La mémoire est effacée lorsque l'élément mémorisé devient indisponible (par exemple, s'il est supprimé, masqué, désactivé, inerte ou exclu du groupe de mise au point).
Désactiver (focusgroup="none")
Utilisez focusgroup="none" pour exclure un élément et son sous-arbre de la navigation par flèche d'un groupe de mise au point ancêtre. L'élément désactivé et son sous-arbre restent accessibles à l'aide de la touche Tabulation, mais les touches fléchées les ignorent :
<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>
La flèche vers la droite permet d'accéder à "Nouveau", puis à "Ouvrir", "Enregistrer", "Fermer" et "Quitter", en ignorant complètement les boutons "Aide" et "Raccourcis". Toutefois, un utilisateur peut toujours accéder à ces boutons en appuyant sur la touche de tabulation pour accéder à la section d'aide. Essayez-le en direct : Concepts supplémentaires > Segments de désactivation avec focusgroup="none".
Schémas courants
Tablist
Contrôle d'onglet avec navigation par touches fléchées entre les onglets.
<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>
Essayez-le en direct : Modèle de liste d'onglets > Liste d'onglets horizontale avec retour à la ligne.
Points à noter :
- L'attribut
focusgroupstartse trouve dans l'onglet selected, de sorte que le focus y entre toujours. - Le modificateur
nomemorygarantit que même si l'utilisateur s'était précédemment concentré sur un autre onglet, la réentrée se fait toujours sur l'onglet sélectionné. - Le modificateur
inlinelimite la navigation par flèche aux touches gauche et droite uniquement. Cela correspond au comportement attendu décrit dans le modèle d'onglets APG. - Le modificateur
wrappermet aux utilisateurs d'utiliser les touches fléchées en continu dans tous les onglets. - Le code du développeur, omis par souci de concision, gère la sélection proprement dite : mise à jour de
aria-selected, activation/désactivation de la visibilité du panneau et déplacement de l'attributfocusgroupstartlors du changement de sélection.
Menu et barre de menu
Menu vertical simple avec navigation par flèches vers le haut et vers le bas.
<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>
Essayez-le en direct : Menu and Menubar Pattern > Simple Vertical Menu.
Avec le modificateur block, seules les flèches vers le haut et vers le bas permettent de parcourir les éléments. Les flèches vers la gauche et vers la droite sont disponibles pour les comportements que vous définissez (par exemple, l'ouverture de sous-menus). Pour une barre de menu avec des sous-menus imbriqués, chaque niveau est un groupe de sélection indépendant. Essayez-le en direct :
Menu and Menubar pattern > Menubar with Popover Submenus
<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>
Essayez-le en direct : Menu and Menubar Pattern > Menubar with Popover Submenus.
Alors que la barre de menu utilise le modificateur inline pour la navigation vers la gauche et vers la droite, les sous-menus utilisent le modificateur block pour la navigation vers le haut et vers le bas. Les groupes de focus imbriqués sont complètement indépendants et n'interfèrent donc pas les uns avec les autres.
Groupe d'options
Groupe d'options personnalisé avec navigation par touches fléchées et contrôle complet du style.
<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>
Essayez-le en direct : Modèle de groupe d'options > Comparaison : natif vs Focusgroup.
Bien que l'attribut focusgroup gère la navigation à l'aide des touches fléchées, vous devez implémenter le code de sélection. Dans cette démo, le code JavaScript gère l'état coché (à l'aide de l'attribut aria-checked).
Concepts clés
Participation aux groupes de discussion
Tous les descendants séquentiellement sélectionnables de l'élément dont focusgroup est défini sur un comportement valide sont considérés comme faisant partie de ce groupe de sélection. Cela signifie que les éléments avec un tabindex négatif ne sont pas pris en compte, mais que les éléments pouvant être sélectionnés de manière native, tels que <button>, le sont, ainsi que les éléments pour lesquels vous avez spécifié un tabindex non négatif.
Taquet de tabulation
Vous n'avez pas besoin de gérer les valeurs tabindex. Même lorsque plusieurs descendants sont naturellement accessibles au clavier (par exemple, plusieurs éléments <button>), focusgroup les réduit à une seule tabulation. Le navigateur gère l'élément sur lequel la tabulation peut s'effectuer à tout moment. Essayez-le en direct :
Modèle de barre d'outils > Aucune gestion de l'index de tabulation requise.
Mémoire de la dernière sélection
Par défaut, lorsqu'un utilisateur appuie sur la touche Tabulation pour quitter un groupe de focus, puis appuie à nouveau sur la touche Tabulation pour y revenir, le focus revient sur le dernier élément sélectionné. C'est essentiel pour les grandes listes et les barres d'outils afin que les utilisateurs ne perdent pas le fil. Utilisez le modificateur nomemory pour désactiver ce comportement lorsque vous souhaitez que le focus soit toujours restauré sur le premier élément ou, si vous utilisez focusgroupstart, pour contrôler l'élément initialement sélectionné.
Groupes de focus imbriqués
Chaque déclaration focusgroup crée un champ d'application indépendant. Un groupe de mise au point imbriqué est automatiquement désactivé de la navigation par flèche de son ancêtre. Utilisez la touche Tabulation pour passer d'un groupe de sélection à un autre, et les touches fléchées pour naviguer dans le groupe de sélection actuel. Essayez-le en direct : Concepts supplémentaires > Groupes de discussion imbriqués.
Prise en charge de Shadow DOM
Focusgroup s'applique par défaut aux limites du Shadow DOM. Un groupe de focus déclaré sur un hôte fantôme inclut des éléments sélectionnables dans l'arborescence fantôme de cet hôte. Si vous souhaitez désactiver cette fonctionnalité, vous pouvez utiliser focusgroup="none" dans l'arbre fantôme de votre composant.
Gestion des conflits de clés
Certains éléments à l'intérieur d'un groupe de mise au point, comme <input>, <textarea> et d'autres commandes, utilisent les touches fléchées à leurs propres fins. En cas de conflit entre les touches de navigation du groupe de sélection et le comportement des touches fléchées d'un élément natif :
- Les touches fléchées sont utilisées par l'élément interactif (par exemple, pour déplacer le curseur de texte), et focusgroup n'interfère pas.
- Tabulation ou Maj+Tabulation fournissent un mécanisme d'échappement par défaut, permettant à un utilisateur d'utiliser la navigation par tabulation pour "réintégrer" le groupe de focus.
Ces comportements d'échappement ne s'appliquent qu'en cas de conflit réel de touches. Les axes sans conflit ne sont pas affectés. Vous pouvez également appeler preventDefault() sur les événements keydown pour remplacer le comportement des touches fléchées du groupe de mise au point pour des éléments spécifiques. Cela signifie que vous pouvez inclure des entrées et des zones de texte dans un groupe de mise au point sans perturber l'un ou l'autre des comportements.
Si vous ajoutez des gestionnaires de clés à vos propres éléments participant à un groupe de focus, veillez à fournir un mécanisme d'échappement similaire afin que les utilisateurs puissent accéder au reste du groupe.
Découverte des descendants profonds
Les éléments focusgroup ne doivent pas nécessairement être des enfants directs du conteneur focusgroup.
Le navigateur considère que tous les descendants pouvant être sélectionnés séquentiellement (tabindex non négatif) participent au groupe de sélection, sauf s'ils se trouvent dans un groupe de sélection imbriqué ou s'ils ont été désactivés avec 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 navigation avec les touches fléchées fonctionne même si les boutons sont imbriqués dans les wrappers <div> et <span>. Il n'y a pas d'exigence concernant la liste à plat. Les éléments d'encapsulation pour la mise en forme sont donc acceptables.
Essayez-le en direct : Concepts supplémentaires > Descendants profonds.
Intégration à la propriété reading-flow
La navigation séquentielle (touche Tabulation) et directionnelle (touche de direction) respectent la propriété CSS reading-flow lorsqu'elle est présente, en suivant l'ordre de lecture visuel plutôt que l'ordre source du DOM.
Cela permet de s'assurer que la navigation à l'aide des touches fléchées correspond à la mise en page que les utilisateurs voient à l'écran.
<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>
Alors que l'ordre du DOM est A, B, C, l'ordre visuel est C, B, A, car la mise en page utilise flex-direction: row-reverse. Toutefois, comme le code utilise également reading-flow: flex-visual, l'ordre de lecture redevient A, B, C, et focusgroup correspond à cet ordre.
Si vous appuyez sur la touche de tabulation, le curseur se positionne d'abord sur C, puis sur B et enfin sur A si vous appuyez sur la flèche vers la droite. Essayez-le en direct : Concepts supplémentaires > Intégration du flux de lecture CSS.
Accessibilité
Inférence des rôles ARIA
Dans un groupe de discussion, le jeton de comportement est utilisé par le navigateur pour déduire un rôle minimal pour le conteneur et ses éléments participants. Cela signifie que lorsque l'attribut focusgroup est défini sur un élément qui a un rôle générique, le rôle approprié, basé sur le comportement choisi, est appliqué. Les éléments participants de l'élément qui ont un rôle générique ou les boutons qui n'ont pas de rôle que vous avez spécifié verront leur rôle inféré en conséquence. Par exemple, le code HTML suivant :
<div focusgroup="tablist">
<button>Tab 1</button>
<button>Tab 2</button>
<button>Tab 3</button>
</div>
Crée l'arborescence d'accessibilité suivante, même si aucun rôle n'a été défini sur les boutons :
+ tablist
|
+ tab
|
+ tab
|
+ tab
Vous pouvez toujours contrôler le comportement en définissant directement le rôle.
Considérations sur l'accessibilité
Veillez à respecter le comportement que vous avez choisi lors de la création d'un groupe de discussion.
L'utilisation du groupe de discussion doit être aussi proche que possible du comportement que vous avez spécifié. Cela permet de s'assurer que les utilisateurs qui s'appuient sur des outils d'accessibilité peuvent parcourir le contenu et utiliser des commandes personnalisées.
Bien que l'inférence de rôle fournisse de bonnes valeurs par défaut, lorsque vous utilisez des éléments avec des rôles non génériques, veillez à ce qu'ils disposent du rôle approprié pour la fonctionnalité qu'ils fournissent.
Lorsque vous utilisez focusgroup, n'oubliez pas que les utilisateurs peuvent avoir besoin de faire défiler le contenu avec les touches fléchées pour le voir. Un utilisateur de clavier doit toujours pouvoir lire et accéder au contenu de votre page.
Détection de fonctionnalités
Pour commencer à utiliser focusgroup dès aujourd'hui, avant qu'il ne soit entièrement compatible avec les navigateurs, vous pouvez détecter la compatibilité de focusgroup en JavaScript :
if ('focusgroup' in HTMLElement.prototype) {
// focusgroup is supported.
} else {
// fall back to manual roving tabindex.
}
Conclusion
L'attribut focusgroup est en cours de normalisation. Nous développons activement le prototype dans Chromium et affinons l'API.
Essayez-le et signalez un problème de groupe de discussion dans l'outil de suivi des problèmes Open-UI GitHub. Nous aimerions particulièrement connaître votre avis sur les points suivants :
- La surface de l'API vous semble-t-elle adaptée aux modèles que vous créez ?
- Y a-t-il des schémas ou des scénarios qui nous échappent ?
- Existe-t-il des éléments sur lesquels l'attribut focusgroup ne devrait pas être autorisé ?
- Comment l'accessibilité fonctionne-t-elle pour vos cas d'utilisation ?
Merci de nous aider à améliorer la navigation au clavier sur le Web !
En savoir plus
- Explication sur les groupes de discussion
- Démonstrations interactives (source)
- Problème HTML WHATWG
- Problèmes liés au groupe de discussion sur l'UI ouverte
- Guide des bonnes pratiques de création ARIA
Merci à Mason Freed, Sara Higley, Scott O'Hara et au reste de la communauté Open-UI pour leur aide dans le retour de focusgroup.