Accordion with Opt-Out Panels
Up/Down arrows navigate between accordion headers. Panel
content is opted out
of arrow navigation with focusgroup="none", but
interactive elements inside panels
remain reachable via Tab.
The focusgroup attribute provides
declarative arrow-key navigation for
composite widgets. Try these documentation
links
and —
they're still reachable via Tab,
but arrow keys skip right over this panel.
Configure your focusgroup with tokens:
inline, block,
wrap, no-memory, and
more. Check the
focusgroup
explainer
for the latest syntax.
Explore nested focusgroups, grid navigation, and
CSS reading-flow integration
on the additional
concepts page.
The focusgroup attribute is
experimental. Enable
Experimental Web Platform features in
about://flags
in Microsoft Edge or another Chromium-based browser.
role="group"overrides thetoolbarrole that the behavior token would otherwise infer on the container- The
<button>items keep their nativebuttonrole — no child role override is needed because thetoolbarbehavior has no child role inference focusgroup="none"opts panel content out of arrow-key navigation- Up/Down arrows move only between headers, skipping panels entirely
- Tab still reaches focusable content inside expanded panels (links, inputs, buttons)
- JavaScript handles
aria-expandedtoggling andhiddenvisibility
View source
<div focusgroup="toolbar block" role="group"
aria-label="Accordion sections">
<h3>
<button type="button" class="expanded" aria-expanded="true"
aria-controls="acc-panel1">Section 1: Getting
Started</button>
</h3>
<div focusgroup="none" role="region" id="acc-panel1">
<p>Panel content with <a href="#">links</a> and
<input type="text" placeholder="inputs"> that remain
tabbable but are skipped by arrow keys.</p>
</div>
<h3>
<button type="button" aria-expanded="false"
aria-controls="acc-panel2">Section 2:
Configuration</button>
</h3>
<div focusgroup="none" role="region" id="acc-panel2" hidden>
<p>More panel content...</p>
</div>
<h3>
<button type="button" aria-expanded="false"
aria-controls="acc-panel3">Section 3: Advanced
Usage</button>
</h3>
<div focusgroup="none" role="region" id="acc-panel3" hidden>
<p>Even more content...</p>
</div>
</div>
Long Content: A Possible Mitigation
Because focusgroup arrow keys jump directly between headers, long panel content can be missed entirely. If a panel has more content than fits on screen, the user may never see it when arrowing through headers.
One possible mitigation: When a section is
expanded, move focus into the panel.
The panel is a focusable scrollable region
(tabindex="0") with
focusgroup="none", so arrow keys scroll its
content instead of
jumping to the next header. The user can press Tab
to move back
to the accordion headers when they're done reading.
Other approaches — such as requiring an activation step before
navigating away,
showing a "skip to next section" link inside the panel, or
avoiding focusgroup
entirely for content-heavy accordions — may be more appropriate
depending on
the use case.
When focusgroup is used on an accordion, arrow keys navigate between section headers. This provides fast movement through the accordion — but it also means that any content between headers is completely skipped by arrow-key navigation.
For short panels this is fine — the user can see the content without scrolling. But if a panel contains a lengthy article, a long list of options, or other tall content, the user may never encounter it at all. Pressing ↓ on the expanded header jumps straight to the next header, bypassing everything in between.
This is a known consideration in the focusgroup design. The explainer notes: "Authors should be aware that since focusgroup navigation takes priority over scrolling, they should take care when constructing large focusgroups to avoid a situation where content can be missed when jumping from one item to another."
The solution is for authors to provide an explicit
path into the
panel content. By making the panel focusable and moving
focus into it
on expansion, the user's arrow keys scroll the content
rather than
navigating the focusgroup, because the panel is opted
out via
focusgroup="none".
Step 1: Make the panel focusable
Add tabindex="0" to the panel element.
This makes it a
focusable scrollable region. Screen readers will
announce it as a region
with its aria-label.
Step 2: Opt out of the focusgroup
Add focusgroup="none" to the panel.
This ensures that
when the panel has focus, arrow keys scroll its content
rather than
navigating the ancestor focusgroup.
Step 3: Move focus on expand
When the user expands a section (click or Enter/Space), JavaScript moves focus to the panel. This puts the user directly into the content where arrow keys will scroll.
Step 4: Let Tab return to headers
Because the panel is opted out of the focusgroup, pressing Tab exits the panel and enters the next focusgroup segment. The user is back on the accordion headers and can arrow-navigate to another section.
This pattern ensures that long content is never silently skipped, while still preserving fast header-to-header navigation when the user wants it.
Not every accordion needs this mitigation. Consider using it when:
- Panel content is likely taller than the viewport
- Panels contain long-form text, articles, or documentation
- Panels contain many interactive controls that the user needs to reach
- Users are expected to read panel content sequentially
For short panels (a few lines of text, a small number of controls), the basic accordion pattern from Demo 1 is sufficient. The user can see the entire panel without scrolling, and Tab still reaches any interactive content inside.
The key question is: will the user miss important content if arrow keys skip over this panel? If the answer is yes, use the focus-into-panel mitigation shown here.
- Expanding a section moves focus into the panel — arrow keys now scroll content instead of jumping headers
- The panel is a focusable scrollable region
(
tabindex="0"+overflow: auto+ constrained height) focusgroup="none"on the panel ensures arrow keys are not consumed by the ancestor focusgroup- Tab exits the panel and returns to the accordion headers
- This prevents long content from being silently skipped by focusgroup navigation
View source
<div focusgroup="toolbar block" role="group"
aria-label="Articles">
<h3>
<button type="button" aria-expanded="false"
aria-controls="long-panel1">The Focusgroup
Problem</button>
</h3>
<div focusgroup="none" role="region" tabindex="0"
id="long-panel1" class="panel-scrollable" hidden
aria-label="The Focusgroup Problem">
<p>Long panel content goes here...</p>
</div>
<!-- more sections ... -->
</div>
<script>
// On expand, move focus into the panel so arrow keys scroll
accordion.querySelectorAll("h3 button").forEach(function (btn) {
btn.addEventListener("click", function () {
if (btn.getAttribute("aria-expanded") === "true") {
var panel = document.getElementById(
btn.getAttribute("aria-controls")
);
if (panel) panel.focus();
}
});
});
</script>
Key Point: focusgroup="none"
The none value opts an element and its subtree out
of the ancestor focusgroup.
This is crucial for composite widgets like accordions where:
- You want arrow keys to move between headers only
- Panel content (links, inputs, buttons) should remain reachable via Tab
- Without
none, arrow keys would navigate into panel content too