Accordion
Collapsible panels. Single-open (default) or multiple-open (data-bi-accordion-multiple).
Overview
Accordion manages vertical space by letting users expand only the sections they care about. It displays a stack of collapsible panels where each panel has a clickable header and a hidden body that reveals on activation. Accordion is used for FAQ pages, settings groups, product specifications, progressive disclosure in complex forms, data-filtering options, dashboard help panels, ecommerce product details, and ERP/POS configuration sections across the Byteion ecosystem.
Why this exists
Without a shared Accordion pattern, teams build ad-hoc collapse panels with inconsistent behavior — some use details/summary, others build custom JS toggle panels, and the chevron rotation, focus management, and ARIA attributes are often forgotten. Accordion provides a consistent, accessible, and framework-optional collapsible panel system that works identically in HTML, React, and Vue with baked-in keyboard navigation, ARIA support, and single/multi-open modes.
Mental model
- Single-open mode — only one panel is expanded at a time. When a new panel opens, the previously open panel closes automatically. Use this for space-saving scenarios like FAQ or filter panels.
- Multi-open mode — multiple panels can remain open simultaneously. Each panel toggles independently. Use this for settings pages where users need to compare or reference multiple sections.
- The chevron — rotates to indicate panel state. Down (or right) means closed; up (or down) means open. The icon is a visual affordance, not the only indicator — the header text should clearly describe the panel content.
- Controlled vs uncontrolled — the Accordion can manage its own state internally (uncontrolled, with
defaultValue) or reflect external state (controlled, withvalueandonValueChange).
Accordion vs Tabs vs Stepper
- Accordion — inline disclosure where content appears below the header. Use for sections that users can read in any order, like FAQ, settings, or filters.
- Tabs — content panels that replace each other without scroll. Use for mutually exclusive views where the user stays in the same viewport position.
- Stepper — sequential steps with required ordering and validation. Use for multi-step forms where each step must be completed before advancing.
If the content is always needed and users should scan it all without toggling, do not use Accordion — keep the content visible. If panels represent linearly dependent steps, use Stepper instead.
When to use
- FAQ pages — questions as headers, answers as panel bodies, single-open mode for compact layout
- Settings groups — each category (Profile, Notifications, Billing) in its own panel, multi-open for comparing settings
- Product specifications — technical details behind expandable labels for clean product pages
- Progressive disclosure — complex forms where optional sections are hidden until needed
- Filter and search panels — collapsible filter groups for data tables and dashboards
- Ecommerce product details — shipping info, size guide, material care behind expandable sections
- ERP / POS configuration — module settings, tax configuration, payment gateway options grouped by category
When not to use
- Do not use for content that is always needed — if every panel contains critical information users need to see, keep it visible and use Section or Stack instead
- Do not use for navigation — accordion is for inline content disclosure, not page navigation. Use Navbar, Sidebar, or Tabs for navigation
- Do not use for sequential forms — if each panel depends on the previous one, use Stepper with validation
- Do not hide all content by default — at least one panel should be open initially to orient the user
- Do not deeply nest accordions — nested collapsible panels create a confusing UX. Use a single level of accordion or restructure the information
Basic example
A compact FAQ accordion with three questions and single-open mode. The first panel is open by default:
<div class="divide-y divide-border">
<div>
<button class="flex w-full items-center justify-between px-5 py-4 text-sm text-foreground hover:bg-muted/30" aria-expanded="true" aria-controls="panel-1" id="trigger-1">
<span>What is Byteion?</span>
<i class="ti ti-chevron-down text-muted-foreground transition-transform"></i>
</button>
<div id="panel-1" role="region" aria-labelledby="trigger-1" class="px-5 pb-4 text-sm text-muted-foreground">
Byteion is a design system of 60+ components with HTML, React, and Vue support.
</div>
</div>
<div>
<button class="flex w-full items-center justify-between px-5 py-4 text-sm text-foreground hover:bg-muted/30" aria-expanded="false" aria-controls="panel-2" id="trigger-2">
<span>Can I use it with Tailwind?</span>
<i class="ti ti-chevron-down text-muted-foreground transition-transform"></i>
</button>
<div id="panel-2" role="region" aria-labelledby="trigger-2" class="px-5 pb-4 text-sm text-muted-foreground"></div>
</div>
</div>
Single-open and multi-open
The Accordion supports two interaction modes via the type prop:
| Mode | Behavior | Best for |
|---|---|---|
| single | Only one panel open at a time. Opening a new panel closes the current one. | FAQ, filters, progressive disclosure |
| multiple | Multiple panels can be open simultaneously. Each panel toggles independently. | Settings pages, comparison panels |
Anatomy
- Group wrapper (Accordion) — the container that manages single/multi-open state and connects panels via shared context
- Item (AccordionItem) — one collapsible unit with a header and body
- Header (AccordionTrigger) — the clickable button with chevron icon. Has
aria-expandedand controls the panel - Body (AccordionContent) — the collapsible content area with
role="region"andaria-labelledby - Chevron — rotates to indicate open/closed state. Uses
ti ti-chevron-downwith atransition-transformrotation
Usage guidelines
- Keep panel headers concise — 1–5 words that clearly describe the panel content
- Start with the first panel open to give users an immediate sense of the accordion pattern
- Use single-open mode for space-saving (FAQ, filters, specifications)
- Use multi-open mode for settings and configuration panels where users need to reference multiple sections
- Do not hide critical form inputs inside collapsed panels. Required fields should always be visible
- Use dividers (
divide-y divide-border) to separate panels visually - If using accordion for a form, do not clear form data when collapsing a panel
Do and Don't
Do
- Use single-open mode for FAQ and filter panels
- Keep panel headers concise and descriptive
- Start with the first panel open as a visual cue
- Use chevron rotation to signal open/closed state
- Add
aria-expandedandaria-controlsfor accessibility
Don't
- Hide critical or required content behind collapsed panels
- Use accordion for page navigation — use Navbar or Sidebar instead
- Nest accordions more than one level deep
- Clear form state when collapsing a panel
- Use accordion for sequential form steps — use Stepper instead
Accessibility
The Accordion follows the WAI-ARIA Accordion pattern. Each trigger is a <button> element with:
aria-expanded—truewhen the panel is open,falsewhen closedaria-controls— references theidof the panel it controlsid— each trigger has a unique ID referenced by the panel'saria-labelledby- Panel region — each body has
role="region"andaria-labelledbypointing to the trigger ID - Keyboard navigation — Enter or Space toggles the panel. Tab and Shift+Tab move between triggers
- Chevron alone is not sufficient — the
aria-expandedattribute must communicate state to screen readers
Customization
Accordion styling follows Byteion layout tokens. Borders use divide-border / border-border, chevron color follows text-muted-foreground, and hover states use hover:bg-muted/30. Customize border radius, spacing, and icon rotation via standard Tailwind utility classes. The chevron transition can be replaced with any animation library — the ti ti-chevron-down icon is a convention, not a requirement.
HTML example
Static accordion requires JavaScript for toggle behavior. Use @byteion/js with data-bi-accordion or your own toggle script:
<div class="divide-y divide-border rounded-xl border border-border" data-bi-accordion="single">
<div data-bi-accordion-item>
<button class="flex w-full items-center justify-between px-5 py-4 text-sm text-foreground hover:bg-muted/30" data-bi-accordion-trigger aria-expanded="false" aria-controls="panel-1" id="trigger-1">
<span>Billing settings</span>
<i class="ti ti-chevron-down text-muted-foreground transition-transform"></i>
</button>
<div id="panel-1" role="region" aria-labelledby="trigger-1" class="px-5 pb-4 text-sm text-muted-foreground" data-bi-accordion-content>
Manage your payment methods, invoices, and billing history.
</div>
</div>
<div data-bi-accordion-item>
<button class="flex w-full items-center justify-between px-5 py-4 text-sm text-foreground hover:bg-muted/30" data-bi-accordion-trigger aria-expanded="false" aria-controls="panel-2" id="trigger-2">
<span>Team permissions</span>
<i class="ti ti-chevron-down text-muted-foreground transition-transform"></i>
</button>
<div id="panel-2" role="region" aria-labelledby="trigger-2" class="px-5 pb-4 text-sm text-muted-foreground" data-bi-accordion-content>
Invite team members and configure access levels.
</div>
</div>
</div>
React example
React exports Accordion, AccordionItem, AccordionTrigger, and AccordionContent from @byteion/react/client. Use type="single" for single-open mode and type="multiple" for multi-open:
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@byteion/react/client';
export default function SettingsAccordion() {
return (
<Accordion type="single" defaultValue="billing" className="divide-y divide-border rounded-xl border border-border">
<AccordionItem value="billing">
<AccordionTrigger>Billing settings</AccordionTrigger>
<AccordionContent>
Manage your payment methods, invoices, and billing history.
</AccordionContent>
</AccordionItem>
<AccordionItem value="team">
<AccordionTrigger>Team permissions</AccordionTrigger>
<AccordionContent>
Invite team members and configure access levels.
</AccordionContent>
</AccordionItem>
<AccordionItem value="notifications">
<AccordionTrigger>Notification preferences</AccordionTrigger>
<AccordionContent>
Choose which updates you receive via email or in-app.
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
Vue example
Vue exports the same Accordion API from @byteion/vue. Use default-value for the initially open panel:
<script setup lang="ts">
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@byteion/vue';
</script>
<template>
<Accordion type="single" default-value="billing" class="divide-y divide-border rounded-xl border border-border">
<AccordionItem value="billing">
<AccordionTrigger>Billing settings</AccordionTrigger>
<AccordionContent>
Manage your payment methods, invoices, and billing history.
</AccordionContent>
</AccordionItem>
<AccordionItem value="team">
<AccordionTrigger>Team permissions</AccordionTrigger>
<AccordionContent>
Invite team members and configure access levels.
</AccordionContent>
</AccordionItem>
</Accordion>
</template>
API Reference
The Accordion component is available as a compound set in both @byteion/react/client and @byteion/vue:
| Component | Props / Emits | Description |
|---|---|---|
| Accordion | type, value, defaultValue, onValueChange | Root wrapper that manages panel state |
| AccordionItem | value | Individual collapsible panel wrapper |
| AccordionTrigger | (none) | Clickable button that toggles the panel |
| AccordionContent | (none) | Collapsible panel body |
Developer tips
- Use controlled mode for URL-synced state — store the open panel value in query params via
useSearchParamsorvue-router queryfor deep-linkable accordion panels - Do not clear form state on collapse — if form inputs live inside accordion panels, preserve their values when collapsing and re-expanding
- Use
type="multiple"for settings — settings panels often need cross-reference; multi-open lets users keep multiple sections visible - Animate the chevron with
transition-transform— a smooth 200–300ms rotation feels natural and gives users visual feedback - Test with long panel labels — long trigger text should wrap or truncate gracefully without breaking the layout
- Test keyboard navigation — verify Tab moves between triggers and Enter/Space toggles panels in both single and multi-open modes
Common mistakes
- Hiding critical content behind collapsed panels — required form fields, legal notices, and pricing info should always be visible
- Using accordion for page navigation — accordion is for inline content disclosure, not navigating between pages. Use Navbar or Sidebar
- Using accordion for sequential forms — if each step depends on the previous one, use Stepper with proper validation
- Starting with all panels closed — at least one panel should be open by default to orient the user to the accordion pattern
- Nested accordions — nested collapsible panels create a confusing visual hierarchy. Restructure the content or use tabs
- Missing ARIA attributes —
aria-expanded,aria-controls,aria-labelledby, androle="region"are required for screen reader support
Production usage tips
- Test both single and multi-open modes — verify that single mode closes the previous panel and that multi mode allows multiple panels open simultaneously
- Test dark mode and branded themes — borders, dividers, and chevron colors use token values that adapt automatically
- Test keyboard accessibility — verify Tab navigation, Enter/Space toggle, and focus management (focus must remain on the trigger, not jump to content)
- Test with JavaScript disabled — static accordion should not break the page; all content should be visible in a degraded state or fallback gracefully
- Test mobile layout — accordion panels should stack vertically on small screens without layout shift
- Keep React and Vue APIs aligned with the core HTML schema —
type,value, anddefaultValueprops map todata-bi-accordionattributes
Premium examples
Pro patterns available in Byteion Pro:
- Accordion with animated height transition — smooth CSS
grid-template-rowsanimation for panel open/close without JS height calculations - Accordion with step validation indicators — each panel shows a completion badge (checkmark, warning, error) based on form validation state
- Nested accordion for deep hierarchies — grouped accordion panels with expandable sub-panels for complex documentation or help centers
- FAQ schema accordion — accordion panels with structured data (
FAQPageschema) for SEO on public docs - Settings page accordion with saved state — panels remember their open/closed state via localStorage between sessions
- Ecommerce product details accordion — shipping, returns, sizing, and care instructions in a single-open accordion below the product description
- ERP / POS configuration accordion — tax settings, payment gateways, and module toggles grouped in a multi-open settings accordion
Related components
- Tabs — mutually exclusive content panels that replace each other without scroll
- Stepper — sequential multi-step flow with validation and required ordering
- Card — surface container for grouping related content
- Drawer — slide-in panel for secondary content and workflows
- Section — vertical spacing between content blocks
API Reference
data-bi-component="accordion" for JS behavior.Related components
Runtime events
Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.
HTML / JS events
bi:accordion:hidebi:accordion:show
React callbacks
onValueChange(value: string[]) => void
Vue emits
update:modelValue[v: string | string[]]
Remote / AJAX
This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.
Explore Pro Components