Components
Accordion
Standard collapsible section primitive for dense dashboard details, panels, and expandable groups.
Overview
Accordion is the shared-ui primitive for collapsible sections in the web dashboard.
It is exported as a set of separate building blocks rather than a dot-notation compound component:
AccordionAccordionGroupAccordionTriggerAccordionContentAccordionHeaderAccordionTitleAccordionSubtitleAccordionRightAccordionIndicator
In practice this primitive is used for:
- expandable sections inside panels
- dense task and project detail groups
- collapsible management sections with actions in the header
- standardized titled sections that should persist open state
Basic Usage
import {
Accordion,
AccordionContent,
AccordionHeader,
AccordionIndicator,
AccordionTitle,
AccordionTrigger,
} from '@fsai/shared-ui';
<Accordion defaultExpanded>
<AccordionTrigger>
<AccordionHeader>
<AccordionTitle>Dependencies</AccordionTitle>
</AccordionHeader>
<AccordionIndicator />
</AccordionTrigger>
<AccordionContent>
<div className="pt-3">{/* expandable content */}</div>
</AccordionContent>
</Accordion>That is the baseline composition used throughout the brand dashboard.
Exported API
| Part | Purpose |
|---|---|
Accordion | Root state container for one accordion item. Supports controlled and uncontrolled open state. |
AccordionGroup | Simple wrapper for stacking multiple accordion items together. |
AccordionTrigger | Clickable and keyboard-accessible header row that toggles the item. |
AccordionContent | Animated height and fade content region. |
AccordionHeader | Left-side title/subtitle wrapper used inside the trigger. |
AccordionTitle | Standard title text. |
AccordionSubtitle | Secondary header text below or beside the title. |
AccordionRight | Right-aligned header content, commonly actions and the indicator. |
AccordionIndicator | Rotating chevron indicator, customizable by icon and className. |
Props
Accordion
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Required. Usually a trigger plus content. |
className | string | — | Additional item classes. |
defaultOpen | boolean | false | Initial open state in uncontrolled mode. |
open | boolean | — | Controlled open state. |
onOpenChange | (open: boolean) => void | — | Change handler for controlled usage. |
storageKey | string | — | Persists open state to localStorage. |
disabled | boolean | false | Prevents toggling. |
unstyled | boolean | false | Removes default item chrome. |
AccordionTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Required trigger content. Usually AccordionHeader, AccordionRight, and AccordionIndicator. |
className | string | — | Additional trigger classes. |
unstyled | boolean | false | Removes default trigger padding and layout styles. |
disabled | boolean | false | Disables toggling for this trigger. |
...htmlProps | HTMLAttributes<HTMLDivElement> | — | Standard div props. Includes tabIndex. |
AccordionContent
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Required expandable content. |
containerClassName | string | — | Classes for the outer animated height wrapper. |
className | string | — | Classes for the inner content wrapper. |
unstyled | boolean | false | Removes default inner padding. |
transition | { duration?: number; ease?: Easing } | — | Overrides the default height and fade animation timing. |
unmountOnExit | boolean | true | Unmounts content when closed instead of keeping it hidden. |
Common Composition Patterns
Standard Section Header
The most common layout is:
AccordionTriggerAccordionHeaderon the leftAccordionRighton the right when extra actions are neededAccordionIndicatoras the final affordance
Controlled Section State
Use controlled state when a parent feature needs to open or close a section based on external events:
<Accordion
open={dependenciesExpanded}
onOpenChange={onDependenciesExpandedChange}
defaultOpen={hasUnresolvedBlockers}
>
<AccordionTrigger>
<AccordionHeader>
<AccordionTitle>Dependencies</AccordionTitle>
</AccordionHeader>
<AccordionIndicator />
</AccordionTrigger>
<AccordionContent>
<TaskDependencies />
</AccordionContent>
</Accordion>Persistent Open State
Use storageKey when a section should remember whether it was open:
<Accordion defaultOpen storageKey="catalog-item-manuals-accordion">
{/* trigger + content */}
</Accordion>This is especially useful in repeated panel sections where the user benefits from the UI remembering their preference.
Brand Dashboard Usage Patterns
Representative usages:
| Pattern | Path | Notes |
|---|---|---|
| Controlled panel section | fsai/apps/brand-dashboard/src/modules/tasks/components/TaskPanel/TaskPanelDetails/TaskPanelDetails.tsx | Uses controlled open state for dependencies and uncontrolled defaultOpen for approvals. |
| Persisted management section | fsai/apps/brand-dashboard/src/modules/menu/components/MenuItemPanel/MenuItemPanelDetails/MenuItemManualsSection.tsx | Shows storageKey, subtitle text, actions in AccordionRight, and a trailing indicator. |
| Dense project phase rows | fsai/apps/brand-dashboard/src/pages/Projects/ProjectDetail/PhaseView/PhaseView.primitives.tsx | Uses unstyled header pieces plus data-accordion-no-toggle for nested interactive controls. |
| Source primitive | fsai/packages/shared-ui/src/primitives/Accordion.tsx | Defines the animation, persistence, accessibility attributes, and escape hatches. |
Important Conventions
AccordionTriggerandAccordionContentmust be rendered insideAccordionor the internal context throws.Accordionsupports both controlled and uncontrolled state. If you passopen, you should also passonOpenChange.storageKeypersists the open state directly inlocalStorage.AccordionTriggersuppresses toggling when the click originates from nested interactive elements such asbutton,a,input,select, or anything inside[data-accordion-no-toggle="true"].- Use
data-accordion-no-toggle="true"when you place custom interactive controls inside the trigger and need to prevent accidental toggling. AccordionGroupis only a layout wrapper. It does not implement single-open behavior.AccordionContentdefaults tounmountOnExit, which means closed content is removed from the DOM unless you opt out.unstyledis important for advanced layouts like project phase rows that want the behavior but not the default chrome.
Guidelines
- Do use
Accordionfor standardized expandable sections in panels, settings, and dense detail views - Do use
AccordionHeader,AccordionTitle, andAccordionSubtitleinstead of rebuilding header text styles by hand - Do use
AccordionRightfor action buttons, badges, and the indicator - Do use
storageKeywhen remembering open state improves repeated workflows - Don't assume
AccordionGroupgives you single-open accordion behavior - Don't put clickable controls inside the trigger without considering toggle suppression