A vertically stacked set of interactive headings that each reveal a section of content.
npm install @radix-ui/react-accordion'use client'
import { createStyleContext } from '@pallas-ui/style-context'
import type { Assign, WithFixedClassName } from '@pallas-ui/style-context'
import * as RadixAccordion from '@radix-ui/react-accordion'
import { Slot } from '@radix-ui/react-slot'
import { type AccordionVariantProps, accordion } from '@styled-system/recipes'
import type { ComponentProps, JsxStyleProps } from '@styled-system/types'
import { ChevronDown } from 'lucide-react'
import * as React from 'react'
const { withProvider, withContext } = createStyleContext(accordion)
export type RootProps = WithFixedClassName<
ComponentProps<typeof RadixAccordion.Root> & {
collapsible?: boolean
}
>
export const Root = withProvider<
React.ComponentRef<typeof RadixAccordion.Root>,
Assign<RootProps, AccordionVariantProps>
>(RadixAccordion.Root, 'root')
export const Item = withContext<
React.ComponentRef<typeof RadixAccordion.Item>,
Assign<WithFixedClassName<ComponentProps<typeof RadixAccordion.Item>>, JsxStyleProps>
>(RadixAccordion.Item, 'item')
export const ItemHeader = withContext<
React.ComponentRef<typeof RadixAccordion.Header>,
Assign<WithFixedClassName<ComponentProps<typeof RadixAccordion.Header>>, JsxStyleProps>
>(RadixAccordion.Header, 'itemHeader')
export const ItemTrigger = withContext<
React.ComponentRef<typeof RadixAccordion.Trigger>,
Assign<WithFixedClassName<ComponentProps<typeof RadixAccordion.Trigger>>, JsxStyleProps>
>(RadixAccordion.Trigger, 'itemTrigger')
export const ItemContent = withContext<
React.ComponentRef<typeof RadixAccordion.Content>,
Assign<WithFixedClassName<ComponentProps<typeof RadixAccordion.Content>>, JsxStyleProps>
>(RadixAccordion.Content, 'itemContent')
type ItemIndicatorBaseProps = React.ComponentPropsWithoutRef<'span'> & {
asChild?: boolean
rotateOnOpen?: boolean
}
const ItemIndicatorBase = React.forwardRef<HTMLSpanElement, ItemIndicatorBaseProps>(
({ children, asChild, rotateOnOpen = true, ...props }, ref) => {
const Comp = asChild ? Slot : 'span'
return (
<Comp ref={ref} data-rotate={String(rotateOnOpen)} {...props}>
{children ?? <ChevronDown />}
</Comp>
)
},
)
ItemIndicatorBase.displayName = 'AccordionItemIndicator'
export const ItemIndicator = withContext<
HTMLSpanElement,
Assign<WithFixedClassName<ComponentProps<typeof ItemIndicatorBase>>, JsxStyleProps>
>(ItemIndicatorBase, 'itemIndicator')
const Accordion = {
Root,
Item,
ItemHeader,
ItemTrigger,
ItemContent,
ItemIndicator,
}
export default Accordionimport Accordion from '@/components/ui/accordion'<Accordion.Root type="single" collapsible>
<Accordion.Item value="item-1">
<Accordion.ItemHeader>
<Accordion.ItemTrigger>Is it accessible?</Accordion.ItemTrigger>
<Accordion.ItemIndicator />
</Accordion.ItemHeader>
<Accordion.ItemContent>
Yes. It adheres to the WAI-ARIA design pattern.
</Accordion.ItemContent>
</Accordion.Item>
</Accordion.Root>The accordion ships with four variants: default, subtle, bordered, and plain.
Three sizes are available: sm, md (default), and lg.
Use type="multiple" to allow multiple items to be open at the same time.
Control the open state externally using value and onValueChange.
Use asChild on Accordion.ItemIndicator to swap the default chevron for any custom icon.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'default' | 'subtle' | 'bordered' | 'plain' | 'default' | Visual style of the accordion |
| size | 'sm' | 'md' | 'lg' | 'md' | Controls padding and text size |
| type | 'single' | 'multiple' | — | Whether one or multiple items can be open at a time |
| collapsible | boolean | false | Allow closing the open item (single mode only) |
| value | string | string[] | — | Controlled open state |
| defaultValue | string | string[] | — | Initial open state (uncontrolled) |
| onValueChange | (value: string | string[]) => void | — | Callback when the open state changes |
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | — | Unique identifier for the item (required) |
| disabled | boolean | false | Disables interaction with the item |
| Prop | Type | Default | Description |
|---|---|---|---|
| disabled | boolean | false | Disables the trigger button |
| asChild | boolean | false | Render as child element instead of a button |
| Prop | Type | Default | Description |
|---|---|---|---|
| forceMount | boolean | false | Keep content mounted in the DOM even when closed |
| asChild | boolean | false | Render as child element |
| Prop | Type | Default | Description |
|---|---|---|---|
| rotateOnOpen | boolean | true | Whether the indicator rotates when the item is open |
| asChild | boolean | false | Replace the default ChevronDown with a custom element |
Adheres to the Accordion WAI-ARIA design pattern.
| Key | Description |
|---|---|
Space | When focus is on an Accordion.ItemTrigger of a collapsed section, expands the section. |
Enter | When focus is on an Accordion.ItemTrigger of a collapsed section, expands the section. |
Tab | Moves focus to the next focusable element. |
Shift + Tab | Moves focus to the previous focusable element. |
ArrowDown | Moves focus to the next Accordion.ItemTrigger. |
ArrowUp | Moves focus to the previous Accordion.ItemTrigger. |
Home | When focus is on an Accordion.ItemTrigger, moves focus to the first Accordion.ItemTrigger. |
End | When focus is on an Accordion.ItemTrigger, moves focus to the last Accordion.ItemTrigger. |
default
subtle
bordered
plain
sm
md
lg