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

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

jsx
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

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.

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 default type to "single" and write data-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", Panel aria-hidden="false", omit hidden. Keep the pair in sync via id / aria-controls / aria-labelledby.
  • Closed (default): aria-expanded="false", aria-hidden="true", hidden on Panel.
  • Open (wrappers): Item open. Do not set open on Trigger or Panel.
  • Disabled (HTML): aria-disabled="true" on Trigger. Wrappers: Item disabled.
  • Heading level: Default h3; match the page outline.
  • Ids: Root mcr:accordion:<rootId>. Each item's Trigger/Panel share one <id> as mct: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:accordion id and data-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.

See also