Nested Focusgroups
A nested focusgroup creates an independent navigation scope within an outer focusgroup. Each group has its own axis, wrapping, memory, and entry point. Each focusgroup is its own tab stop; arrows navigate within the active group.
- Each focusgroup is a separate tab stop in the page's tab order
- Arrow keys navigate within whichever group currently has focus
- Sub-groups within a toolbar use
role="group"rather than anotherrole="toolbar", as nesting toolbars is architecturally unsound. Arrow-key navigation works on the inner group regardless of which explicitroleit carries.
View source
<div focusgroup="toolbar" aria-label="Main toolbar"
class="toolbar-horizontal">
<button type="button">Save</button>
<button type="button">Print</button>
<div role="group" focusgroup="toolbar" tabindex="0" aria-label="Text formatting"
class="nested-toolbar">
<button type="button">Bold</button>
<button type="button">Italic</button>
<button type="button">Underline</button>
</div>
<button type="button">Close</button>
<button type="button">Exit</button>
</div>
Opt-Out Segments with focusgroup="none"
Use focusgroup="none" to exclude a subtree's focusable
descendants from arrow navigation while keeping them reachable
via Tab.
- Arrow keys navigate: New → Open → Save → Close → Exit (skipping Help & Shortcuts)
- Tab still reaches Help and Shortcuts
- The dimmed buttons visually indicate they're in a
focusgroup="none"zone
View source
<div focusgroup="toolbar"
aria-label="Segmented toolbar" class="toolbar-horizontal">
<button type="button">New</button>
<button type="button">Open</button>
<button type="button">Save</button>
<span focusgroup="none">
<button type="button" class="opted-out-button">Help</button>
<button type="button" class="opted-out-button">Shortcuts</button>
</span>
<button type="button">Close</button>
<button type="button">Exit</button>
</div>
Deep Descendants
Focusgroup items don't need to be direct children. The browser
discovers focusable
descendants at any depth (unless they are inside a nested
focusgroup or focusgroup="none").
<div> and
<span> wrappers.
- No flat list requirement; wrapper elements for styling are fine
View source
<div focusgroup="toolbar"
aria-label="Nested wrappers" class="toolbar-horizontal">
<div>
<span><button type="button">Alpha</button></span>
<span><button type="button">Beta</button></span>
<span><button type="button">Gamma</button></span>
</div>
</div>
CSS reading-flow Integration
When reading-flow: flex-visual is set, the browser
uses the visual flex order for all focus navigation,
including focusgroup arrow keys, rather than
DOM source order.
reading-flow: flex-visual, arrow navigation
follows the visual left-to-right order (C → B → A), not the
DOM order (A → B → C).
reading-flow: flex-visualmakes the browser use visual flex order for focus navigation (Tab and arrow keys)- Right arrow from C goes to B, then A (visual left-to-right)
- Without
reading-flow, arrow keys follow DOM order (A → B → C)
View source
<div focusgroup="toolbar" aria-label="Visual order"
class="reading-flow-toolbar">
<button type="button">A (DOM first)</button>
<button type="button">B (DOM second)</button>
<button type="button">C (DOM third)</button>
</div>
Feature Detection
Check whether the browser supports focusgroup
before relying on it.
If unsupported, you can fall back to a JavaScript-based roving
tabindex or show a notice.
View source
<script>
if ('focusgroup' in HTMLElement.prototype) {
// focusgroup is supported - use it!
console.log('focusgroup is supported');
} else {
// Not supported - fall back to JS roving tabindex
console.log('focusgroup is NOT supported');
}
</script>