Disclosure
Disclosure (Show/Hide)
A single button that shows or hides a section of content, without accordion grouping semantics.
Last verified against WCAG 2.2 and WAI-ARIA APG 1.2 on 2026-07-02.
Implementation
disclosure-pattern.tsx
function DisclosurePattern() {
const [expanded, setExpanded] = useState(false);
const panelId = "disclosure-pattern-panel";
return (
<div>
<button
aria-expanded={expanded}
aria-controls={panelId}
onClick={() => setExpanded(e => !e)}
>
Advanced options
</button>
<div id={panelId} hidden={!expanded}>
{/* panel content */}
</div>
</div>
);
}Live demo
Required roles, states & properties
| Element | Attribute | Why |
|---|---|---|
| Trigger <button> | aria-expanded | Communicates whether the panel is currently visible — announced as "expanded" or "collapsed." Must update synchronously with every toggle. |
| Trigger <button> | aria-controls | Associates the trigger with the panel id it toggles, so AT can relate the two even though they aren't nested. |
| Panel | hidden attribute | Removes the panel and everything inside it from the accessibility tree and the tab order while collapsed — stronger than a CSS-only visual hide. |
Keyboard interaction model
| Key | Behavior |
|---|---|
| Tab / Shift+Tab | Moves focus to and from the trigger button; panel content is only reachable via Tab when the panel is expanded. |
| Enter / Space | Toggles the panel open or closed. |
Focus management rules
- Panel content is only in the Tab order while expanded — collapsed content is never reachable.
- Toggling the panel never moves focus away from the trigger.
- There is no arrow-key model — a disclosure is a single control, not a group like Accordion.
WCAG 2.2 success criteria mapping
| SC | Name | Level | Why it applies |
|---|---|---|---|
| 2.1.1 | Keyboard | A | All functionality is operable through a keyboard interface with no specific timing. |
| 4.1.2 | Name, Role, Value | A | For all UI components, name, role, and value are programmatically determinable; states and changes are announced. |