UNPKG

@loke/ui

Version:
208 lines (159 loc) 6.07 kB
--- name: accordion type: core domain: navigation requires: [loke-ui] description: > Expandable section groups with type=single (exclusive) or type=multiple (independent). AccordionItem/Header/Trigger/Content composition. CSS variable animation via --loke-accordion-content-height. Keyboard navigation (Home/End/Arrow keys). collapsible prop only applies to type=single. AccordionContent proxies --loke-collapsible-content-height into --loke-accordion-content-height. --- # Accordion ## Setup Single-mode accordion — one item open at a time, closeable. ```tsx import { Accordion, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent, } from "@loke/ui/accordion"; function FaqAccordion() { return ( <Accordion type="single" collapsible defaultValue="item-1"> <AccordionItem value="item-1"> <AccordionHeader> <AccordionTrigger>What is @loke/ui?</AccordionTrigger> </AccordionHeader> <AccordionContent> A headless React UI primitives library with granular subpath exports. </AccordionContent> </AccordionItem> <AccordionItem value="item-2"> <AccordionHeader> <AccordionTrigger>Is it accessible?</AccordionTrigger> </AccordionHeader> <AccordionContent> Yes. Keyboard navigation, ARIA attributes, and roving focus are built in. </AccordionContent> </AccordionItem> </Accordion> ); } ``` ## Core Patterns ### Multiple mode All items can be open simultaneously. The `collapsible` prop is irrelevant here — items are always independently togglable. ```tsx <Accordion type="multiple" defaultValue={["item-1", "item-3"]}> <AccordionItem value="item-1"> <AccordionHeader> <AccordionTrigger>Section A</AccordionTrigger> </AccordionHeader> <AccordionContent>Content A</AccordionContent> </AccordionItem> <AccordionItem value="item-2"> <AccordionHeader> <AccordionTrigger>Section B</AccordionTrigger> </AccordionHeader> <AccordionContent>Content B</AccordionContent> </AccordionItem> </Accordion> ``` ### CSS variable animation `AccordionContent` measures the content and sets `--loke-accordion-content-height` (and `--loke-accordion-content-width`). Use these in CSS — never use `max-height` with a fixed value. ```css .accordion-content { overflow: hidden; } .accordion-content[data-state="open"] { animation: accordion-open 200ms ease-out; } .accordion-content[data-state="closed"] { animation: accordion-close 200ms ease-in; } @keyframes accordion-open { from { height: 0; } to { height: var(--loke-accordion-content-height); } } @keyframes accordion-close { from { height: var(--loke-accordion-content-height); } to { height: 0; } } ``` `data-state` is `"open"` or `"closed"` on `AccordionItem`, `AccordionHeader`, `AccordionTrigger`, and `AccordionContent`. ### Horizontal orientation Flips keyboard navigation to use ArrowLeft/ArrowRight instead of ArrowUp/ArrowDown. ```tsx <Accordion type="single" collapsible orientation="horizontal"> <AccordionItem value="tab-1"> <AccordionHeader> <AccordionTrigger>Panel 1</AccordionTrigger> </AccordionHeader> <AccordionContent>Content 1</AccordionContent> </AccordionItem> </Accordion> ``` ### Controlled state ```tsx const [value, setValue] = useState(""); <Accordion type="single" collapsible value={value} onValueChange={setValue}> <AccordionItem value="a"> <AccordionHeader> <AccordionTrigger>Item A</AccordionTrigger> </AccordionHeader> <AccordionContent>Content A</AccordionContent> </AccordionItem> </Accordion> ``` For `type="multiple"`, `value` is `string[]` and `onValueChange` receives `string[]`. ## Common Mistakes ### Missing `type` prop — TypeScript error and undefined runtime behavior `Accordion` is a discriminated union on `type`. Omitting it is a compile error and will not render correctly. ```tsx // Wrong — type is required <Accordion> <AccordionItem value="a">...</AccordionItem> </Accordion> // Correct <Accordion type="single" collapsible> <AccordionItem value="a">...</AccordionItem> </Accordion> ``` Source: `src/components/accordion/accordion.tsx` — type discriminator on `AccordionSingleProps | AccordionMultipleProps`. ### `collapsible` with `type="multiple"` — silently ignored `collapsible` is only on `AccordionImplSingleProps`. Passing it to a `type="multiple"` accordion has no effect — multiple items are always independently collapsible. ```tsx // collapsible prop does nothing here <Accordion type="multiple" collapsible>...</Accordion> ``` Source: `src/components/accordion/accordion.tsx` — `collapsible` only on `AccordionImplSingle`. ### Missing `value` on `AccordionItem` — open/close state breaks silently Each `AccordionItem` needs a unique string value. Without it, the accordion cannot track which item is open. ```tsx // Wrong <AccordionItem>...</AccordionItem> // Correct <AccordionItem value="unique-key">...</AccordionItem> ``` Source: `src/components/accordion/accordion.tsx` — `valueContext.value.includes(value)` check. ### Animating with `max-height` instead of CSS variables — janky transitions The component measures actual content height and provides it as a CSS variable. Hardcoded `max-height` causes jumpy easing because the value does not match actual height. ```css /* Wrong */ .accordion-content[data-state="open"] { max-height: 500px; } /* Correct */ .accordion-content[data-state="open"] { height: var(--loke-accordion-content-height); } ``` Source: `src/components/collapsible/collapsible.tsx` — `--loke-collapsible-content-height` dimension measurement, proxied by `AccordionContent`. ## Cross-references - **Collapsible** (`@loke/ui/collapsible`) — use for a single expandable section; Accordion wraps Collapsible internally - **Choosing the Right Component**Accordion vs Collapsible decision guidance - **Tabs** (`@loke/ui/tabs`) — for switching between panels where only one panel is ever visible