Accordion
Vertical stack of headers; each header reveals a panel when activated. Wrappers default to single-open by writing data-mode="single". In HTML, set data-mode explicitly: core treats a missing attribute as multi-open (only the exact value "single" enables close-others).
HTML authors set ARIA on Trigger and Panel. React/Vue wrappers take open / disabled on Item (and type on Root); they mint ids and write matching ARIA.
Composition
Root
Item (one or more; direct children of Root)
Header (first element child of Item)
Trigger (first element child of Header; must be a button)
Panel (after Header)Usage
HTML
<div id="mcr:accordion:faq" data-mode="single">
<div>
<h3>
<button type="button" id="mct:accordion:q1" aria-expanded="false" aria-controls="mcc:accordion:q1">
What is monochrome?
</button>
</h3>
<div id="mcc:accordion:q1" role="region" aria-labelledby="mct:accordion:q1" aria-hidden="true" hidden>
A minimal component library...
</div>
</div>
<div>
<h3>
<button type="button" id="mct:accordion:q2" aria-expanded="false" aria-controls="mcc:accordion:q2">
How does it work?
</button>
</h3>
<div id="mcc:accordion:q2" role="region" aria-labelledby="mct:accordion:q2" aria-hidden="true" hidden>
Components are server-rendered...
</div>
</div>
</div>React
import { Accordion } from "@monochrome-ui/react"
<Accordion.Root>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger>What is monochrome?</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>A minimal component library...</Accordion.Panel>
</Accordion.Item>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger>How does it work?</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>Components are server-rendered...</Accordion.Panel>
</Accordion.Item>
</Accordion.Root>Vue
<script setup lang="ts">
import { Accordion } from "@monochrome-ui/vue"
</script>
<template>
<Accordion.Root>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger>What is monochrome?</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>A minimal component library...</Accordion.Panel>
</Accordion.Item>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger>How does it work?</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel>Components are server-rendered...</Accordion.Panel>
</Accordion.Item>
</Accordion.Root>
</template>Parts
Root
Required. Element: div. Children: one or more Item (direct children).
| Kind | Name | Type / value | Default | Description |
|---|---|---|---|---|
| attr | id |
mcr:accordion:<rootId> |
Accordion root id. Core reads data-mode here. |
|
| attr | data-mode |
"single" | "multiple" |
Single-open vs multi-open. Always set in HTML. Absent or any value other than "single" is multi-open in core. Wrappers always write this from type. |
|
| prop | type |
"single" | "multiple" |
"single" |
Maps to data-mode. Prefer this prop in React/Vue. |
Use multiple when several panels may stay open at once.
Item
Required. Element: div. Children: Header then Panel.
| Kind | Name | Type | Default | Description |
|---|---|---|---|---|
| prop | open |
boolean |
false |
Initial open on mount. Flows to Trigger aria-expanded and Panel aria-hidden / hidden. After interaction, DOM ARIA is truth. |
| prop | disabled |
boolean |
false |
Applies aria-disabled on Trigger; core skips activation. |
Header
Required. Element: h3 (or h1-h6). Children: Trigger only (first element child).
| Kind | Name | Type | Default | Description |
|---|---|---|---|---|
| prop | as |
"h1" … "h6" |
"h3" |
Heading level. Change only to match the document outline. |
Trigger
Required. Element: button.
| Kind | Name | Type / value | Default | Description |
|---|---|---|---|---|
| attr | type |
"button" |
||
| attr | id |
mct:accordion:<id> |
Must match Panel aria-labelledby. |
|
| attr | aria-expanded |
"true" | "false" |
"false" |
Whether this item's panel is open. |
| attr | aria-controls |
mcc:accordion:<id> |
||
| attr | aria-disabled |
"true" | absent |
absent | When "true", core ignores activation. HTML only; wrappers use Item disabled. |
Panel
Required. Element: div.
| Kind | Name | Type / value | Default | Description |
|---|---|---|---|---|
| attr | id |
mcc:accordion:<id> |
||
| attr | role |
"region" |
||
| attr | aria-labelledby |
mct:accordion:<id> |
||
| attr | aria-hidden |
"true" | "false" |
"true" |
Inverse of paired Trigger aria-expanded. |
| attr | hidden |
present | absent | present when closed | Omit when open. |
Notes
- data-mode: Always set
"single"or"multiple"in HTML. Omission is multi-open in core. Wrappers defaulttypeto"single"and writedata-mode. - Structure: Items must be direct children of Root. Item children are Header then Panel. Trigger is Header's first element child button. Extra wrappers break single-mode and Arrow/Home/End.
- Open (HTML): Trigger
aria-expanded="true", Panelaria-hidden="false", omithidden. Keep the pair in sync via id /aria-controls/aria-labelledby. - Closed (default):
aria-expanded="false",aria-hidden="true",hiddenon Panel. - Open (wrappers): Item
open. Do not setopenon Trigger or Panel. - Disabled (HTML):
aria-disabled="true"on Trigger. Wrappers: Itemdisabled. - Heading level: Default
h3; match the page outline. - Ids: Root
mcr:accordion:<rootId>. Each item's Trigger/Panel share one<id>asmct:accordion:<id>/mcc:accordion:<id>. Wrappers mint<id>per Item; HTML authors choose unique ids. - Nesting: An Accordion Root may live inside a Panel; the inner Root needs its own
mcr:accordionid anddata-mode.
Keyboard
| Key | Behavior |
|---|---|
| Enter / Space | Toggle focused item (via synthesized click) |
| ArrowDown | Focus next item's Trigger |
| ArrowUp | Focus previous item's Trigger |
| Home | Focus first item's Trigger |
| End | Focus last item's Trigger |
Disabled triggers are skipped. Roving walks Item siblings under Root.