@remix-run/ui/accordion
accordion provides headless disclosure-set primitives for grouped settings, FAQ sections, and dense panels where each item owns a trigger and content region. App code owns the markup and visual styles.
Usage
import * as accordion from '@remix-run/ui/accordion'
import { contentStyle, headingStyle, itemStyle, rootStyle, triggerStyle } from './accordion.styles'
export function SettingsAccordion() {
return (
<accordion.Context defaultValue="shipping">
<div mix={[rootStyle, accordion.root()]}>
<accordion.ItemContext value="shipping">
<div mix={[itemStyle, accordion.item()]}>
<h3 mix={headingStyle}>
<button mix={[triggerStyle, accordion.trigger()]} type="button">
Shipping
</button>
</h3>
<div mix={[contentStyle, accordion.content()]}>
Delivery windows and carrier defaults.
</div>
</div>
</accordion.ItemContext>
<accordion.ItemContext value="billing">
<div mix={[itemStyle, accordion.item()]}>
<h3 mix={headingStyle}>
<button mix={[triggerStyle, accordion.trigger()]} type="button">
Billing
</button>
</h3>
<div mix={[contentStyle, accordion.content()]}>Review billing details.</div>
</div>
</accordion.ItemContext>
</div>
</accordion.Context>
)
}Use type="multiple" when more than one panel may stay open. defaultValue and value are arrays in multiple mode. Control the open value with value and onValueChange, or let the context own it with defaultValue.
Set collapsible={false} in single mode when the open item must stay open. The locked-open trigger receives aria-disabled.
Listen for bubbling changes on the root or an ancestor:
<div
mix={accordion.onAccordionChange((event) => {
console.log(event.accordionType, event.itemValue, event.value)
})}
>
{/* accordion */}
</div>@remix-run/ui/accordion
Context: provider for controlled or uncontrolled accordion state. Supportsvalue,defaultValue,onValueChange,disabled,collapsible, andtype="multiple".ItemContext: provider for one itemvalue. Passdisabledto prevent that item from opening or receiving keyboard focus.root(): wires the root element and bubbling change events.item(): wires one item wrapper.trigger(): wires the item trigger, keyboard navigation, and trigger ARIA attributes.content(): wires the item panel id, hidden state, inert state, and open or closed state attributes.onAccordionChange(...): event mixin for the bubblingAccordionChangeEvent.AccordionChangeEvent: bubbling event withvalue,itemValue, andaccordionType.AccordionType,AccordionValue,AccordionSingleValue,AccordionMultipleValue, andAccordionHeadingLevel: public state and configuration types.AccordionBaseContextProps,AccordionSingleContextProps,AccordionMultipleContextProps,AccordionContextProps,AccordionRootOptions,AccordionItemOptions,AccordionTriggerOptions, andAccordionContentOptions: context prop and mixin option types.
Behavior Notes
- Single mode stores one open value or
null. Multiple mode stores an array of open values. - Single accordions are collapsible by default. Set
collapsible={false}to keep the open item locked open. - Root
disableddisables every item. Itemdisabledonly disables that item. - Arrow keys move between enabled triggers.
HomeandEndmove to the first and last enabled triggers. - Disabled items are skipped by keyboard navigation.
- Trigger and panel ids are generated and linked with
aria-controls,aria-labelledby, andaria-expanded. Closed panels receivearia-hiddenandinert. - Each item and trigger receives
data-state="open"ordata-state="closed"for app-owned styling. AccordionChangeEventbubbles from the root and includesvalue,itemValue, andaccordionType.