Accordion
since v0.2.0Collapsible content container — use for FAQs and progressive disclosure sections.
Supports both uncontrolled (defaultValue) and controlled (value + onValueChange) modes.
Install
Add the package and import the component.
pnpm add @hey-mike/tungstenpnpm add @hey-mike/tungstenimport { Accordion } from '@hey-mike/tungsten';import { Accordion } from '@hey-mike/tungsten';Preview
Same fixtures used by the visual-regression suite.
Usage
apps/docs/app/snapshots/accordion/page.tsx
import {
Accordion,
AccordionItem,
AccordionTrigger,
AccordionPanel,
} from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';
import { HeroSpecimen } from '../_components/HeroSpecimen';
const items = [
{ value: 'a', label: 'Section A', body: 'Body of section A.' },
{ value: 'b', label: 'Section B (open)', body: 'Body of section B — visible because defaultValue is "b".' },
{ value: 'c', label: 'Section C', body: 'Body of section C.' },
];
export default function AccordionSnapshot() {
return (
<VariantGrid
title="Accordion"
hero={
<HeroSpecimen>
<Accordion defaultValue="b">
<AccordionItem value="a">
<AccordionTrigger>Section A</AccordionTrigger>
<AccordionPanel className="text-ink-2 pb-3 text-sm">Body of section A.</AccordionPanel>
</AccordionItem>
<AccordionItem value="b">
<AccordionTrigger>Section B</AccordionTrigger>
<AccordionPanel className="text-ink-2 pb-3 text-sm">Body of section B.</AccordionPanel>
</AccordionItem>
</Accordion>
</HeroSpecimen>
}
variants={[
{
label: 'one open',
node: (
<Accordion defaultValue="b" className="w-[480px]">
{items.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger>{item.label}</AccordionTrigger>
<AccordionPanel className="pb-4 text-sm leading-relaxed text-ink-2">
{item.body}
</AccordionPanel>
</AccordionItem>
))}
</Accordion>
),
},
]}
/>
);
}
import {
Accordion,
AccordionItem,
AccordionTrigger,
AccordionPanel,
} from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';
import { HeroSpecimen } from '../_components/HeroSpecimen';
const items = [
{ value: 'a', label: 'Section A', body: 'Body of section A.' },
{ value: 'b', label: 'Section B (open)', body: 'Body of section B — visible because defaultValue is "b".' },
{ value: 'c', label: 'Section C', body: 'Body of section C.' },
];
export default function AccordionSnapshot() {
return (
<VariantGrid
title="Accordion"
hero={
<HeroSpecimen>
<Accordion defaultValue="b">
<AccordionItem value="a">
<AccordionTrigger>Section A</AccordionTrigger>
<AccordionPanel className="text-ink-2 pb-3 text-sm">Body of section A.</AccordionPanel>
</AccordionItem>
<AccordionItem value="b">
<AccordionTrigger>Section B</AccordionTrigger>
<AccordionPanel className="text-ink-2 pb-3 text-sm">Body of section B.</AccordionPanel>
</AccordionItem>
</Accordion>
</HeroSpecimen>
}
variants={[
{
label: 'one open',
node: (
<Accordion defaultValue="b" className="w-[480px]">
{items.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger>{item.label}</AccordionTrigger>
<AccordionPanel className="pb-4 text-sm leading-relaxed text-ink-2">
{item.body}
</AccordionPanel>
</AccordionItem>
))}
</Accordion>
),
},
]}
/>
);
}
Props
Surface specific to <Accordion />.
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "single" | "multiple" | — | — |
| defaultValue | string | string[] | — | Initial open section key. Uncontrolled mode. Initial open section keys. Uncontrolled mode. |
| value | string | string[] | null | — | Controlled open section key. Pass null for "nothing open".
Controlled open section keys. Pass [] for "nothing open". |
| onValueChange | ((value: string | null) => void) | ((value: string | null) => void) | ((value: string[]) => void) | ((value: string[]) => void) | — | Optional in uncontrolled mode — fires on every toggle for observation.
Required in controlled mode. Fires with the newly open key, or null when collapsed.
Optional in uncontrolled mode — fires with the next open-key array.
Required in controlled mode. Fires with the next open-key array. |
Sub-components
Composition slots re-exported from the same module.
AccordionItem
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | — | Unique key for this section. Matches defaultValue or value on the root Accordion. |
AccordionGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "single" | "multiple" | — | — |
| defaultValue | string | string[] | — | Initial open section key. Uncontrolled mode. Initial open section keys. Uncontrolled mode. |
| value | string | string[] | null | — | Controlled open section key. Pass null for "nothing open".
Controlled open section keys. Pass [] for "nothing open". |
| onValueChange | ((value: string | null) => void) | ((value: string | null) => void) | ((value: string[]) => void) | ((value: string[]) => void) | — | Optional in uncontrolled mode — fires on every toggle for observation.
Required in controlled mode. Fires with the newly open key, or null when collapsed.
Optional in uncontrolled mode — fires with the next open-key array.
Required in controlled mode. Fires with the next open-key array. |
AccordionTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| icon | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Optional leading icon. Pass a ReactElement (e.g. a lucide-react icon). |
| showChevron | boolean | — | Show the built-in chevron. Defaults to true. Pass false to suppress. |