Components · Data display & feedback
Accordion
Vertically stacked interactive items that expand and collapse to reveal or hide content. New component in v1.4.
Preview
What is Zetta?
A token-first design system delivered as a skill — three themes and ~70 components.
How do I use it?
Install the plugin and let your AI apply the skill in your own stack.
Is there a package?
No — Zetta ships tokens and specs, not a shared component library.
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Accordion" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `accordion` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `accordion` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
┌───────────────────────────────────────┐
│ Accordion Item 1 header [▼] │ ← accordion-trigger (closed)
├───────────────────────────────────────┤
│ Accordion Item 2 header [▲] │ ← accordion-trigger (open)
│ Content for item 2... │ ← accordion-content
│ More content here. │
├───────────────────────────────────────┤
│ Accordion Item 3 header [▼] │ ← accordion-trigger (closed)
└───────────────────────────────────────┘
When to use
- FAQ sections.
- Settings panels with grouped options.
- Navigation sub-menus in sidebar or documentation.
- Long-form content that benefits from progressive disclosure.
When not to use
- When all content should be visible — use a plain list.
- When the user needs to compare content across sections — collapsed content makes comparison harder.
- When there are fewer than 3 items — just show the content.
Do
- Keep accordion trigger labels concise — they should tell the user exactly what's inside.
- Default the most important section to open in forms or settings.
- Use
type="single"for FAQ (only one answer at a time);type="multiple"for settings (user may need multiple sections).
Don't
- Don't nest accordions more than one level deep.
- Don't use accordion for primary page navigation.
- Don't use accordions for content that users need to scan across sections simultaneously.
- Trigger labels: noun or short question. "Billing", "How do I reset my password?".
- Keep triggers scannable — users read triggers to decide which to open.
- Content body: any component is valid inside accordion content.
- Avoid putting critical information inside a closed accordion — it may be missed.
- Do not truncate trigger text — wrap instead.
Keyboard
| Key | Behavior |
|---|---|
Tab |
Move focus to the next accordion trigger |
Shift+Tab |
Move focus to the previous accordion trigger |
Enter / Space |
Toggle the focused item's expanded state |
Down Arrow |
Move focus to the next trigger |
Up Arrow |
Move focus to the previous trigger |
Home |
Move focus to the first trigger |
End |
Move focus to the last trigger |
Arrow key navigation between triggers is an enhancement, not a replacement for Tab.
Accessibility
- Trigger ARIA:
role="button",aria-expanded="true/false",aria-controls="panel-id" - Panel ARIA:
role="region",aria-labelledby="trigger-id",id="panel-id" tabIndex={0}on each trigger so each is independently focusable in natural document order.- Contrast:
{colors.ink}on transparent ({colors.canvas}) is 18.92:1 (AAA).{colors.primary}on{colors.primary-bg}open state is 5.42:1 (AA).
Spec
Values and token references resolved straight from the Zetta spec.
accordion-root
- width
- 100%
- borderRadius
- {rounded.base}
- overflow
- hidden
accordion-item
- borderBottom
- 1px solid {colors.hairline}
accordion-item-last
- borderBottom
- none
accordion-trigger
- display
- flex
- alignItems
- center
- justifyContent
- space-between
- width
- 100%
- paddingX
- {spacing.md}
- paddingY
- {spacing.sm}
- backgroundColor
- transparent
- textColor
- {colors.ink}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 500
- lineHeight
- 1.5
- cursor
- pointer
- borderRadius
- 0
- transition
- background-color {motion.duration-fast} {motion.easing-default}
accordion-trigger-hover
- backgroundColor
- {colors.surface-muted}
- textColor
- {colors.ink}
accordion-trigger-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- -2px
accordion-trigger-open
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
accordion-trigger-selected
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
- borderLeft
- 2px solid {colors.primary}
accordion-trigger-disabled
- textColor
- {colors.disabled-text}
- cursor
- not-allowed
- opacity
- 0.5
accordion-icon
- iconSize
- {icons.sizeMap.sm}
- textColor
- inherit
- transition
- transform {motion.duration-fast} {motion.easing-default}
accordion-icon-open
- transform
- rotate(180deg)
accordion-content
- paddingX
- {spacing.md}
- paddingBottom
- {spacing.md}
- paddingTop
- 0
- textColor
- {colors.body}
- fontFamily
- Geist
- fontSize
- 14px
- lineHeight
- 1.6
- overflow
- hidden
- transition
- height {motion.duration-base} {motion.easing-default}