Skip to main content

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

ComponentPlacementRole
AccordionTop-level wrapper.Owns state, keyboard navigation, and shared options.
Accordion ItemDirect child of Accordion.The runtime looks for direct item children when it initializes.
Accordion HeaderInside Accordion Item.Provides heading semantics (<h2><h6>) for the trigger.
Accordion TriggerUsually inside Accordion Header.Interactive control users click or focus to toggle a panel.
Accordion ContentInside Accordion Item, after the header.The panel that opens and closes.

Quick Start

Basic — header-generated trigger
<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.

Explicit structure with custom trigger content
<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

PropTypeDefaultDescription
tagstringdiv

HTML tag for the root element.

Settings Props

PropTypeDefaultDescription
type"single" | "multiple"single

Expansion mode. "single" allows only one item open at a time ( Disclosure pattern). "multiple" allows any number of items open simultaneously.

orientation"vertical" | "horizontal"vertical

Orientation of the accordion. Affects which arrow keys navigate between triggers and how focus moves.

loopbooleantrue

Whether keyboard navigation wraps from the last trigger back to the first, and vice versa.

disabledbooleanfalse

Disables all items in the accordion when true. Individual items can still override this with their own disabled prop.

animationDurationstring"300"

Duration of the expand/collapse animation in milliseconds, as a string.

Styling Props

PropTypeDefaultDescription
classclassome-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:

KeyAction
Enter / SpaceToggle the associated panel.
ArrowDownMove focus to the next trigger (vertical orientation).
ArrowUpMove focus to the previous trigger (vertical orientation).
ArrowRightMove focus to the next trigger (horizontal orientation).
ArrowLeftMove focus to the previous trigger (horizontal orientation).
HomeMove focus to the first trigger.
EndMove 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-expanded and aria-controls pointing to its content panel.
  • Each content panel has role="region" and aria-labelledby pointing to its header.
  • data-ome-state attributes 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

PropTypeDefaultDescription
tagstringdiv

HTML tag for the item wrapper.

Settings Props

PropTypeDefaultDescription
disabledbooleanfalse

Disables this specific item. Overrides the root disabled prop on a per-item basis.

openbooleanfalse

Initial open state. In single mode, only the last item with open="true" will actually render open.

toggleClassstring""

Additional CSS class added to the item when it is in the open state. Useful for CSS-driven animations or visibility changes.

Styling Props

PropTypeDefaultDescription
classclassome-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

PropTypeDefaultDescription
tagstringdiv

HTML tag for the header wrapper.

levelstring"3"

Heading level for the header ("1" through "6"). Renders as <h1><h6> accordingly.

triggerTagstringspan

Tag used for the auto-generated trigger's inner label wrapper when the header creates a trigger from content.label.

Content Props

PropTypeDefaultDescription
labelstring""

Fallback trigger label. When the header slot is empty, this value is used to generate a trigger automatically.

Styling Props

PropTypeDefaultDescription
classclassome-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

PropTypeDefaultDescription
tagstringspan

Tag for the fallback label wrapper only. This does not change the outer button element — the trigger always renders a <button> as its interactive root.

Settings Props

PropTypeDefaultDescription
disabledbooleanfalse

Disables this trigger independently of the item or root disabled setting.

Content Props

PropTypeDefaultDescription
labelstring""

Fallback label text. Used when the trigger slot is empty to render a text label and chevron.

Styling Props

PropTypeDefaultDescription
classclassome-accordion-trigger-default

CSS class applied to the trigger button element.

labelClassclassome-accordion-trigger-default__label

CSS class applied to the label text element inside the trigger.

chevronClassclassome-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

PropTypeDefaultDescription
tagstringdiv

HTML tag for the content panel.

Settings Props

PropTypeDefaultDescription
hiddenUntilFoundbooleanfalse

When true, the collapsed panel uses the browser's hidden="until-found" attribute instead of height: 0. This allows browser find-in-page (Ctrl+F / Cmd+F) to search inside collapsed panels and expand them automatically when a match is found. Use this when your accordion contains searchable reference content.

Styling Props

PropTypeDefaultDescription
classclassome-accordion-content-default

CSS class applied to the content element.


Common Mistakes

Wrapping items in an extra container

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.

Expecting trigger tag to change the button

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.

Using hiddenUntilFound as a visibility toggle

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).