Disclosure
A button that shows and hides an associated content region; uses aria-expanded on the trigger and aria-controls to the region, with DOM show/hide.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when a control shows and hides a single section of related content (e.g., "Show more details", an FAQ answer, an advanced-options or filters panel).
- Use when the revealed content is arbitrary (text, form fields, media).
- Use when a single independent expandable region is toggled by one trigger.
Try a different component when
- Do not use when several coordinated expandable sections form an accordion (use
accordion.basic). - Do not use when the revealed items are in-place commands or actions (use
menu.basic). - Do not use when a single trigger reveals a list of navigation links (use
navigation-menu.dropdown). - Do not use when the control chooses a form value (use
select.native).
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- Use a native
<button>(preferred), orrole="button"only when a native button cannot be used.- If
role="button"is used instead of a native<button>, addtabindex="0"and keyboard support for Enter and Space, ensuring Space prevents page scrolling while activating the control.
- If
- The button reflects open state with
aria-expanded="true|false". - The button references the region with
aria-controls="IDREF". - The region is shown/hidden in the DOM (e.g., via the
hiddenattribute), so that when closed, its content cannot be reached by keyboard or screen readers. - The button has an accessible name that describes the content it controls.
- When the button has visible text, the visible text serves as the accessible name.
- For an icon-only trigger, provide an accessible name using
aria-labeloraria-labelledby.
- The button omits
aria-haspopup. - Ensure a visible focus state (e.g., a 2px solid outline offset by 1-2px) around the trigger.
Donts
Avoid these accessibility and UX barriers.
- Do not use
role="menu"on the region. - Do not add
aria-haspopupto the button. - Do not put the expanded state on the region instead of the trigger.
- Do not leave the region visible while
aria-expanded="false"(and vice versa).
Customizable
Alternatives and options that give the AI agent some room to move.
- The region may take
role="region"with an accessible name, recommended when the region contains headings; avoidrole="region"when it would create landmark proliferation (e.g., more than roughly 6 disclosures on one page). - Enter and Space both toggle the region by virtue of the native
<button>; no extra key handling is required. - A disclosure indicator (e.g., a caret) is decorative and marked
aria-hidden="true".
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
"use client";
export function Disclosure({ label, children }) {
const [open, setOpen] = useState(false);
const regionId = useId();
return (
<div>
<button
type="button"
aria-expanded={open ? "true" : "false"}
aria-controls={regionId}
onClick={() => setOpen(v => !v)}
>
{label}
{/* Decorative caret; state is conveyed by aria-expanded, not the glyph. */}
<span aria-hidden="true">{open ? " [-]" : " [+]"}</span>
</button>
{/* Region is removed from the accessibility tree and tab order when closed. */}
<div id={regionId} hidden={!open}>
{children}
</div>
</div>
);
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
Keyboard
- Tab lands on the trigger button.
- A visible focus indicator appears on the trigger when it receives keyboard focus.
- Enter toggles the region between shown and hidden.
- Space toggles the region between shown and hidden.
- When the region is hidden, focus cannot reach any content inside it.
Screen Reader
- The button announces its expanded or collapsed state via
aria-expanded, which flips between"true"and"false"on every toggle. - The button name describes the content it controls.
- When collapsed, the region content is not reachable in reading order.