FSAI Design System
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:

  • Accordion
  • AccordionGroup
  • AccordionTrigger
  • AccordionContent
  • AccordionHeader
  • AccordionTitle
  • AccordionSubtitle
  • AccordionRight
  • AccordionIndicator

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

PartPurpose
AccordionRoot state container for one accordion item. Supports controlled and uncontrolled open state.
AccordionGroupSimple wrapper for stacking multiple accordion items together.
AccordionTriggerClickable and keyboard-accessible header row that toggles the item.
AccordionContentAnimated height and fade content region.
AccordionHeaderLeft-side title/subtitle wrapper used inside the trigger.
AccordionTitleStandard title text.
AccordionSubtitleSecondary header text below or beside the title.
AccordionRightRight-aligned header content, commonly actions and the indicator.
AccordionIndicatorRotating chevron indicator, customizable by icon and className.

Props

Accordion

PropTypeDefaultDescription
childrenReactNodeRequired. Usually a trigger plus content.
classNamestringAdditional item classes.
defaultOpenbooleanfalseInitial open state in uncontrolled mode.
openbooleanControlled open state.
onOpenChange(open: boolean) => voidChange handler for controlled usage.
storageKeystringPersists open state to localStorage.
disabledbooleanfalsePrevents toggling.
unstyledbooleanfalseRemoves default item chrome.

AccordionTrigger

PropTypeDefaultDescription
childrenReactNodeRequired trigger content. Usually AccordionHeader, AccordionRight, and AccordionIndicator.
classNamestringAdditional trigger classes.
unstyledbooleanfalseRemoves default trigger padding and layout styles.
disabledbooleanfalseDisables toggling for this trigger.
...htmlPropsHTMLAttributes<HTMLDivElement>Standard div props. Includes tabIndex.

AccordionContent

PropTypeDefaultDescription
childrenReactNodeRequired expandable content.
containerClassNamestringClasses for the outer animated height wrapper.
classNamestringClasses for the inner content wrapper.
unstyledbooleanfalseRemoves default inner padding.
transition{ duration?: number; ease?: Easing }Overrides the default height and fade animation timing.
unmountOnExitbooleantrueUnmounts content when closed instead of keeping it hidden.

Common Composition Patterns

Standard Section Header

The most common layout is:

  • AccordionTrigger
  • AccordionHeader on the left
  • AccordionRight on the right when extra actions are needed
  • AccordionIndicator as 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:

PatternPathNotes
Controlled panel sectionfsai/apps/brand-dashboard/src/modules/tasks/components/TaskPanel/TaskPanelDetails/TaskPanelDetails.tsxUses controlled open state for dependencies and uncontrolled defaultOpen for approvals.
Persisted management sectionfsai/apps/brand-dashboard/src/modules/menu/components/MenuItemPanel/MenuItemPanelDetails/MenuItemManualsSection.tsxShows storageKey, subtitle text, actions in AccordionRight, and a trailing indicator.
Dense project phase rowsfsai/apps/brand-dashboard/src/pages/Projects/ProjectDetail/PhaseView/PhaseView.primitives.tsxUses unstyled header pieces plus data-accordion-no-toggle for nested interactive controls.
Source primitivefsai/packages/shared-ui/src/primitives/Accordion.tsxDefines the animation, persistence, accessibility attributes, and escape hatches.

Important Conventions

  • AccordionTrigger and AccordionContent must be rendered inside Accordion or the internal context throws.
  • Accordion supports both controlled and uncontrolled state. If you pass open, you should also pass onOpenChange.
  • storageKey persists the open state directly in localStorage.
  • AccordionTrigger suppresses toggling when the click originates from nested interactive elements such as button, 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.
  • AccordionGroup is only a layout wrapper. It does not implement single-open behavior.
  • AccordionContent defaults to unmountOnExit, which means closed content is removed from the DOM unless you opt out.
  • unstyled is important for advanced layouts like project phase rows that want the behavior but not the default chrome.

Guidelines

  • Do use Accordion for standardized expandable sections in panels, settings, and dense detail views
  • Do use AccordionHeader, AccordionTitle, and AccordionSubtitle instead of rebuilding header text styles by hand
  • Do use AccordionRight for action buttons, badges, and the indicator
  • Do use storageKey when remembering open state improves repeated workflows
  • Don't assume AccordionGroup gives you single-open accordion behavior
  • Don't put clickable controls inside the trigger without considering toggle suppression

On this page