Accordion
A collapsible content panel supporting single and multiple open modes.
Rendering Accordion…
Installation
Copy the source into your project with the CLI. It lands under @/components/ui and is yours to edit.
npx mlola-ui add accordionUsage
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
export default function Example() {
return (
<Accordion type="single" defaultValue="plans" collapsible>
<AccordionItem value="plans">
<AccordionTrigger>Is there a free plan?</AccordionTrigger>
<AccordionContent>Yes. The open-source components are free forever.</AccordionContent>
</AccordionItem>
<AccordionItem value="themes">
<AccordionTrigger>Can I change themes later?</AccordionTrigger>
<AccordionContent>Change data-theme on any ancestor. Nothing else moves.</AccordionContent>
</AccordionItem>
</Accordion>
);
}Styling comes from the engine stylesheet and the nearest theme. Set it once at the root:
<html data-theme="graphite" data-mode="light">HTML
Without React, write this markup and load @mlola-ui/behavior once. It runs the keyboard, focus and state for every element marked data-ml="accordion", including markup added later, and needs no build step.
<div class="ml-accordion" data-ml="accordion">
<article class="ml-accordion-item" data-state="open">
<h3 class="ml-accordion-heading">
<button id="t1" class="ml-accordion-trigger" data-state="open" aria-expanded="true" aria-controls="p1">
<span>Is there a free plan?</span>
<svg class="mlola-glyph" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6"/></svg>
</button>
</h3>
<div id="p1" class="ml-accordion-panel" role="region" aria-labelledby="t1" data-state="open">
<p>Yes. The components in the open-source registry are free forever.</p>
</div>
</article>
<article class="ml-accordion-item" data-state="closed">
<h3 class="ml-accordion-heading">
<button id="t2" class="ml-accordion-trigger" data-state="closed" aria-expanded="false" aria-controls="p2">
<span>Can I change themes later?</span>
<svg class="mlola-glyph" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6"/></svg>
</button>
</h3>
<div id="p2" class="ml-accordion-panel" role="region" aria-labelledby="t2" data-state="closed" hidden>
<p>Change data-theme on any ancestor. Nothing else moves.</p>
</div>
</article>
</div><script type="module">
import { observe } from "@mlola-ui/behavior";
observe();
</script>Options
| Prop | Values | Default |
|---|---|---|
type | singlemultiple | single |
Every prop with a fixed set of values, read from the exported TypeScript types with its doc comment. Open the Code tab above for the rest of the API.
Classes and attributes
A class names each element; data-* and aria-* attributes carry its variant and state. The values listed are the ones the stylesheet draws; the default needs no attribute.
| Class | Attributes |
|---|---|
ml-accordion | — |
ml-accordion-item | data-state="open" |
ml-accordion-heading | — |
ml-accordion-trigger | — |
ml-accordion-trigger-label | — |
ml-accordion-chevron | — |
ml-accordion-panel | — |
ml-accordion-panel-inner | — |
Accessibility and theming
- Semantic elements, accessible names and state relationships are part of the contract.
- Keyboard, pointer and touch paths are operable; focus stays visible in every theme.
- Light and dark modes pass WCAG AA contrast in all 5 canonical themes.
- Reduced motion and forced colors keep meaning and operation.
Dependencies
Installed alongside this component. No third-party UI runtime is ever added.
@mlola-ui/behavior@mlola-ui/icons