YA Accordion
A collapsible accordion component for organizing content into expandable sections
The YaAccordion component provides an accessible way to organize content into collapsible sections. It supports single and multiple open items, with smooth animations and keyboard navigation.
Quick Start
pnpm dlx shadcn@latest add @yavendio/ya-accordionnpx shadcn@latest add @yavendio/ya-accordionyarn dlx shadcn@latest add @yavendio/ya-accordionimport {
YaAccordion,
YaAccordionItem,
YaAccordionTrigger,
YaAccordionContent,
} from '@/components/ui/ya-accordion'
export default function Example() {
return (
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Is it accessible?</YaAccordionTrigger>
<YaAccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>
)
}Basic Usage
<YaAccordion type="single" collapsible className="w-full">
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Product Information</YaAccordionTrigger>
<YaAccordionContent>
<p className="mb-4">
Our flagship product combines cutting-edge technology with sleek design. Built with
premium materials, it offers unparalleled performance and reliability.
</p>
<p>
Key features include advanced processing capabilities, and an intuitive user interface
designed for both beginners and experts.
</p>
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2">
<YaAccordionTrigger>Shipping Details</YaAccordionTrigger>
<YaAccordionContent>
<p className="mb-4">
We offer worldwide shipping through trusted courier partners. Standard delivery takes
3-5 business days, while express shipping ensures delivery within 1-2 business days.
</p>
<p>
All orders are carefully packaged and fully insured. Track your shipment in real-time
through our dedicated tracking portal.
</p>
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-3">
<YaAccordionTrigger>Return Policy</YaAccordionTrigger>
<YaAccordionContent>
<p className="mb-4">
We stand behind our products with a comprehensive 30-day return policy. If you're not
completely satisfied, simply return the item in its original condition.
</p>
<p>
Our hassle-free return process includes free return shipping and full refunds processed
within 48 hours of receiving the returned item.
</p>
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Visual Types
YaAccordion comes with three visual types for different use cases:
| Type | Description | Usage |
|---|---|---|
card | Border with shadow when open | FAQs, content sections, documentation |
folder | Compact with icon, badge, and actions | File managers, navigation trees |
simple | Minimal with no border | Dropdown menus, inline expansion |
Card Type
Folder Type
Simple Type
// Card Type
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1" type="card">
<YaAccordionTrigger>Accordion title</YaAccordionTrigger>
<YaAccordionContent>Content here</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>
// Folder Type
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1" type="folder">
<YaAccordionTrigger leftIcon={<FolderOpen />} badge="99">
Accordion title
</YaAccordionTrigger>
<YaAccordionContent>Items list</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>
// Simple Type
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1" type="simple">
<YaAccordionTrigger>Accordion title</YaAccordionTrigger>
<YaAccordionContent>Content here</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Behavior Types
Single Type
Only one item can be open at a time. The collapsible prop allows closing all items.
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Is it accessible?</YaAccordionTrigger>
<YaAccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2">
<YaAccordionTrigger>Is it styled?</YaAccordionTrigger>
<YaAccordionContent>
Yes. It comes with default styles that matches the YaVendio Design System.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Multiple Type
Multiple items can be open simultaneously.
<YaAccordion type="multiple">
<YaAccordionItem value="item-1">
<YaAccordionTrigger>General Information</YaAccordionTrigger>
<YaAccordionContent>
This accordion allows multiple items to be open at the same time.
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2">
<YaAccordionTrigger>Technical Details</YaAccordionTrigger>
<YaAccordionContent>
The multiple type is controlled by the type="multiple" prop.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Collapsible Behavior
Control whether all items can be closed or if at least one must remain open.
With collapsible (can close all items)
Without collapsible (always one open)
// With collapsible - can close all items
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Can I close this?</YaAccordionTrigger>
<YaAccordionContent>
Yes! Click the trigger again to close this item.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>
// Without collapsible - always one open
<YaAccordion type="single" defaultValue="item-1">
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Can I close this?</YaAccordionTrigger>
<YaAccordionContent>
No. At least one item must remain open.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Default Value
Set which item is open by default using the defaultValue prop.
defaultValue="item-2"<YaAccordion type="single" collapsible defaultValue="item-2">
<YaAccordionItem value="item-1">
<YaAccordionTrigger>First Item</YaAccordionTrigger>
<YaAccordionContent>This item starts closed.</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2">
<YaAccordionTrigger>Second Item (Default Open)</YaAccordionTrigger>
<YaAccordionContent>This item is open by default.</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>States
Controlled State
Use the value and onValueChange props for controlled behavior.
Currently open: item-1
'use client'
import { useState } from 'react'
import { YaAccordion, YaAccordionItem, YaAccordionTrigger, YaAccordionContent } from '@/components/ui/ya-accordion'
export default function ControlledAccordion() {
const [value, setValue] = useState('item-1')
return (
<>
<p>Currently open: {value || 'None'}</p>
<YaAccordion type="single" collapsible value={value} onValueChange={setValue}>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Section 1</YaAccordionTrigger>
<YaAccordionContent>Content for section 1</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2">
<YaAccordionTrigger>Section 2</YaAccordionTrigger>
<YaAccordionContent>Content for section 2</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>
</>
)
}Disabled State
Disable individual accordion items using the disabled prop.
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Available Section</YaAccordionTrigger>
<YaAccordionContent>This section is fully interactive.</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2" disabled>
<YaAccordionTrigger>Disabled Section</YaAccordionTrigger>
<YaAccordionContent>This content cannot be accessed.</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Folder Type
The folder type is designed for hierarchical navigation and file management interfaces.
Basic Folder
import { FolderOpen } from 'lucide-react'
<YaAccordion type="single" collapsible className="space-y-2">
<YaAccordionItem value="item-1" type="folder">
<YaAccordionTrigger leftIcon={<FolderOpen />} badge="3">
AI Research Topics
</YaAccordionTrigger>
<YaAccordionContent>
<div className="space-y-2">
<div className="text-sm">Item 1: The Evolution of AI</div>
<div className="text-sm">Item 2: Ethical Considerations</div>
<div className="text-sm">Item 3: Applications in Healthcare</div>
</div>
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2" type="folder">
<YaAccordionTrigger leftIcon={<FolderOpen />}>
Empty Folder
</YaAccordionTrigger>
<YaAccordionContent>
<div className="text-sm text-muted-foreground">No items yet</div>
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Folder with Actions
import { FolderOpen, MoreVertical } from 'lucide-react'
<YaAccordion type="single" collapsible className="space-y-2">
<YaAccordionItem value="item-1" type="folder">
<YaAccordionTrigger
leftIcon={<FolderOpen />}
badge="12"
actions={
<div
role="button"
tabIndex={0}
className="hover:opacity-70 cursor-pointer"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.stopPropagation()
}
}}
>
<MoreVertical className="size-4" />
</div>
}
>
Project Files
</YaAccordionTrigger>
<YaAccordionContent>
<div className="space-y-2">
<div className="text-sm">📄 README.md</div>
<div className="text-sm">📄 package.json</div>
</div>
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Actions: Use <div role="button"> instead of <button> to avoid nested button issues. Always use onClick={(e) => e.stopPropagation() and handle keyboard events with onKeyDown for accessibility.
Simple Type
The simple type provides a minimal, unobtrusive accordion for inline content expansion.
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1" type="simple">
<YaAccordionTrigger>What is Next.js?</YaAccordionTrigger>
<YaAccordionContent>
Next.js is a React framework that provides building blocks to create web applications.
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="item-2" type="simple">
<YaAccordionTrigger>What is React?</YaAccordionTrigger>
<YaAccordionContent>
React is a JavaScript library for building user interfaces.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Examples
FAQ Section
<YaAccordion type="single" collapsible className="w-full">
<YaAccordionItem value="faq-1">
<YaAccordionTrigger>What payment methods do you accept?</YaAccordionTrigger>
<YaAccordionContent>
We accept all major credit cards, PayPal, and bank transfers for orders over $500.
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="faq-2">
<YaAccordionTrigger>How long does shipping take?</YaAccordionTrigger>
<YaAccordionContent>
Standard shipping takes 3-5 business days. Express and overnight options available.
</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="faq-3">
<YaAccordionTrigger>What is your return policy?</YaAccordionTrigger>
<YaAccordionContent>
We offer a 30-day money-back guarantee. Items must be in original condition.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>With Action Buttons
import { YaButton } from '@/components/ui/ya-button'
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Account Settings</YaAccordionTrigger>
<YaAccordionContent>
<div className="space-y-4">
<p>Manage your account preferences and settings.</p>
<div className="flex gap-2">
<YaButton variant="secondary" size="sm">Edit Profile</YaButton>
<YaButton variant="tertiary" size="sm">Change Password</YaButton>
</div>
</div>
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Nested Accordions
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>Level 1: Main Category</YaAccordionTrigger>
<YaAccordionContent>
<p className="mb-4">This is the main category content.</p>
<YaAccordion type="single" collapsible className="border-l-2 border-primary pl-4">
<YaAccordionItem value="nested-1">
<YaAccordionTrigger>Level 2: Subcategory A</YaAccordionTrigger>
<YaAccordionContent>Content for subcategory A</YaAccordionContent>
</YaAccordionItem>
<YaAccordionItem value="nested-2">
<YaAccordionTrigger>Level 2: Subcategory B</YaAccordionTrigger>
<YaAccordionContent>Content for subcategory B</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Props
YaAccordion
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'single' | 'multiple' | required | Whether one or multiple items can be open |
collapsible | boolean | false | Allow closing all items (single type only) |
defaultValue | string | string[] | - | Default open item(s) (uncontrolled) |
value | string | string[] | - | Controlled open item(s) |
onValueChange | (value: string | string[]) => void | - | Callback when open items change |
disabled | boolean | false | Disable all items |
className | string | - | Additional classes |
YaAccordionItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | required | Unique value for the item |
type | 'card' | 'folder' | 'simple' | 'card' | Visual style type |
disabled | boolean | false | Disable this item |
className | string | - | Additional classes |
YaAccordionTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
leftIcon | ReactNode | - | Icon on the left (folder type) |
badge | string | number | - | Badge counter (folder type) |
actions | ReactNode | - | Action buttons (folder type) |
className | string | - | Additional classes |
children | ReactNode | - | Trigger content |
YaAccordionContent
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes |
forceMount | boolean | false | Keep content mounted when hidden |
children | ReactNode | - | Content to display |
Accessibility
<YaAccordion type="single" collapsible>
<YaAccordionItem value="item-1">
<YaAccordionTrigger>
What is your refund policy?
</YaAccordionTrigger>
<YaAccordionContent>
We offer full refunds within 30 days of purchase.
</YaAccordionContent>
</YaAccordionItem>
</YaAccordion>Best Practices
| Do | Don't |
|---|---|
| Use descriptive, concise trigger text | Generic labels like "Item 1", "Section A" |
| Group related content in single items | Too many nested levels (max 2) |
| Use single type for FAQs | Multiple type for simple Q&A |
| Keep content scannable | Very long content blocks |
| Provide default open for important info | Hide critical information |
TypeScript
import { type VariantProps } from 'class-variance-authority'
import { yaAccordionItemVariants } from '@/components/ui/ya-accordion'
type AccordionItemType = VariantProps<typeof yaAccordionItemVariants>['type']
interface MyComponentProps {
accordionType?: AccordionItemType
}