Accordion
Overview
Use the Accordion family when you need a group of expandable sections with built-in keyboard support, ARIA wiring, and animated panel open and close behavior. The family is composed, not standalone — the root component manages shared state and keyboard navigation, while child components define each individual section. Accordion works well for FAQs, settings panels, sidebar navigation, product feature lists, and any content that benefits from progressive disclosure.
Authoring Structure
Accordion
└── Accordion Item
├── Accordion Header
│ └── Accordion Trigger
└── Accordion Content
Placement Rules
| Component | Placement | Role |
|---|---|---|
| Accordion | Top-level wrapper. | Owns state, keyboard navigation, and shared options. |
| Accordion Item | Direct child of Accordion. | The runtime looks for direct item children when it initializes. |
| Accordion Header | Inside Accordion Item. | Provides heading semantics (<h2> – <h6>) for the trigger. |
| Accordion Trigger | Usually inside Accordion Header. | Interactive control users click or focus to toggle a panel. |
| Accordion Content | Inside Accordion Item, after the header. | The panel that opens and closes. |
Quick Start
<OmeAccordion>
{#slot default}
<OmeAccordionItem>
{#slot default}
<OmeAccordionHeader
structure='{{"level":"2","triggerTag":"strong"}}'
content='{{"label":"Account settings"}}'
/>
<OmeAccordionContent>
{#slot default}<p>Manage your account preferences here.</p>{/slot}
</OmeAccordionContent>
{/slot}
</OmeAccordionItem>
{/slot}
</OmeAccordion>
When the AccordionHeader has an empty slot and a content.label value, it auto-generates a trigger for you — no need to nest AccordionTrigger manually.
<OmeAccordion settings='{{"type":"single"}}'>
{#slot default}
<OmeAccordionItem>
{#slot default}
<OmeAccordionHeader structure='{{"level":"2"}}'>
{#slot default}
<OmeAccordionTrigger>
{#slot default}Shipping{/slot}
</OmeAccordionTrigger>
{/slot}
</OmeAccordionHeader>
<OmeAccordionContent>
{#slot default}
<div class="faq-answer">
<p>Shipping content goes here.</p>
</div>
{/slot}
</OmeAccordionContent>
{/slot}
</OmeAccordionItem>
{/slot}
</OmeAccordion>
Use the explicit structure when you need custom markup inside the trigger — icons, badges, or any content beyond a plain label.
Family Components
Accordion (Root)
The root component wraps all items and manages shared behavior: expansion mode (single or multiple), keyboard navigation, loop wrapping, and animation timing. It does not render any visible UI itself — only a container element.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | div | HTML tag for the root element. |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | "single" | "multiple" | single | Expansion mode. |
orientation | "vertical" | "horizontal" | vertical | Orientation of the accordion. Affects which arrow keys navigate between triggers and how focus moves. |
loop | boolean | true | Whether keyboard navigation wraps from the last trigger back to the first, and vice versa. |
disabled | boolean | false | Disables all items in the accordion when |
animationDuration | string | "300" | Duration of the expand/collapse animation in milliseconds, as a string. |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-accordion-default | CSS class applied to the root element. |
Expansion Modes
single— Opening one item automatically closes the previously open item. Only one panel is visible at a time. This is the classic accordion behavior.multiple— Each item toggles independently. Opening one does not affect others. Use this for disclosure-style UIs where users may want several sections visible.
Keyboard Behavior
When a trigger has focus:
| Key | Action |
|---|---|
Enter / Space | Toggle the associated panel. |
ArrowDown | Move focus to the next trigger (vertical orientation). |
ArrowUp | Move focus to the previous trigger (vertical orientation). |
ArrowRight | Move focus to the next trigger (horizontal orientation). |
ArrowLeft | Move focus to the previous trigger (horizontal orientation). |
Home | Move focus to the first trigger. |
End | Move focus to the last trigger. |
When loop is true, pressing ArrowDown on the last trigger wraps to the first, and ArrowUp on the first wraps to the last. In RTL layouts, horizontal arrow keys are automatically reversed.
Accessibility
The root component generates unique IDs for each item and wires up ARIA attributes automatically:
- Each trigger has
aria-expandedandaria-controlspointing to its content panel. - Each content panel has
role="region"andaria-labelledbypointing to its header. data-ome-stateattributes sync open/closed state for CSS targeting.
Accordion Item
Each AccordionItem represents a single expandable section. It is the direct child of the root Accordion and the parent of AccordionHeader and AccordionContent. The runtime discovers items by scanning for direct item children at initialization time.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | div | HTML tag for the item wrapper. |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Disables this specific item. Overrides the root |
open | boolean | false | Initial open state. In |
toggleClass | string | "" | Additional CSS class added to the item when it is in the open state. Useful for CSS-driven animations or visibility changes. |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-accordion-item-default | CSS class applied to the item element. |
Accordion Header
The header provides semantic heading markup (<h1> – <h6>) for the trigger. It determines the heading level and can auto-generate a trigger when its slot is empty but a content.label value is provided. This is the simplest way to create an accordion — just set the label and move on.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | div | HTML tag for the header wrapper. |
level | string | "3" | Heading level for the header ( |
triggerTag | string | span | Tag used for the auto-generated trigger's inner label wrapper when the header creates a trigger from |
Content Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "" | Fallback trigger label. When the header slot is empty, this value is used to generate a trigger automatically. |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-accordion-header-default | CSS class applied to the header element. |
Header Fallback Rendering
When the header slot is empty and content.label has a value, the header auto-generates a trigger element. This means you can build a working accordion with just AccordionHeader and AccordionContent — no explicit AccordionTrigger needed:
Accordion Header (slot empty + label set)
└── Auto-generated Trigger
└── Label text
└── Chevron icon
Accordion Trigger
The interactive element users click, tap, or focus to toggle a panel. When used inside AccordionHeader, it receives heading semantics from the parent. When the slot is empty, the trigger renders a label wrapper and chevron icon using its content.label value.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | span | Tag for the fallback label wrapper only. This does not change the outer button element — the trigger always renders a |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Disables this trigger independently of the item or root |
Content Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "" | Fallback label text. Used when the trigger slot is empty to render a text label and chevron. |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-accordion-trigger-default | CSS class applied to the trigger button element. |
labelClass | class | ome-accordion-trigger-default__label | CSS class applied to the label text element inside the trigger. |
chevronClass | class | ome-accordion-trigger-default__chevron | CSS class applied to the chevron icon inside the trigger. |
Trigger Fallback Rendering
When the trigger slot is empty and content.label is set, the trigger renders:
<button class="ome-accordion-trigger-default">
<span class="ome-accordion-trigger-default__label">Label text</span>
<span class="ome-accordion-trigger-default__chevron">▼</span>
</button>
Accordion Content
The content panel that expands and collapses. It is animated by default using the root's animationDuration setting. Content is hidden via height: 0 and overflow: hidden when collapsed — it remains in the DOM at all times.
Structure Props
| Prop | Type | Default | Description |
|---|---|---|---|
tag | string | div | HTML tag for the content panel. |
Settings Props
| Prop | Type | Default | Description |
|---|---|---|---|
hiddenUntilFound | boolean | false | When |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-accordion-content-default | CSS class applied to the content element. |
Common Mistakes
Do not place another block between Accordion and AccordionItem. The runtime scans for direct item children at initialization. Adding a wrapper (like a Group or Stack block) between them breaks item discovery and the accordion will not initialize.
AccordionTrigger structure.tag only changes the inner fallback label wrapper — the outer interactive element is always a <button>. If you need a different element, use the trigger slot to provide your own content instead.
AccordionContent settings.hiddenUntilFound is specifically for browser find-in-page integration. It is not a general-purpose visibility toggle and may cause unexpected behavior if used outside its intended purpose.
FAQs
Can I nest accordions inside each other?
Yes. You can place an Accordion inside an AccordionContent panel. Each nested accordion manages its own state independently. Make sure to use appropriate heading levels — if the outer accordion uses level="2", the inner accordion should use level="3" to maintain a correct document outline.
How do I style individual items differently?
Use the class prop on AccordionItem to apply a custom class, and the toggleClass prop to add an additional class when the item is open. You can also target items with CSS using data-ome-state="open" or data-ome-state="closed" attributes that the runtime adds automatically.
Can I start with multiple items open?
In multiple mode, yes — set open="true" on each item you want expanded on load. In single mode, only one item can be open at a time, so only the last item with open="true" will actually render expanded.
What happens if I don't include a Header?
The accordion will still function, but you will lose heading semantics and ARIA labeling. The content panel will not have an aria-labelledby association, which reduces accessibility. Always include an AccordionHeader (or ensure your custom trigger provides equivalent semantics).