Accordion
A set of collapsible panels with headings.
import type { JSX } from '@solidjs/web';
import { Accordion } from 'base-ui-solid/accordion';
import styles from './index.module.css';
export default function ExampleAccordion() {
return (
<Accordion.Root class={styles.Accordion}>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
What is Base UI?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Base UI is a library of high-quality unstyled React components for design systems and
web apps.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
How do I get started?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Head to the “Quick start” guide in the docs. If you’ve used unstyled libraries before,
you’ll feel at home.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
Can I use it for my project?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>Of course! Base UI is free and open source.</div>
</Accordion.Panel>
</Accordion.Item>
</Accordion.Root>
);
}
function PlusIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
stroke-linecap="square"
stroke-linejoin="round"
{...props}
style={
typeof props.style === 'string'
? `display: block; ${props.style}`
: { display: 'block', ...(typeof props.style === 'object' ? props.style : {}) }
}
>
<path d="M1.5 8h13M8 14.5v-13" />
</svg>
);
}
Anatomy
Import the component and assemble its parts:
import { Accordion } from 'base-ui-solid/accordion';
<Accordion.Root>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger />
</Accordion.Header>
<Accordion.Panel />
</Accordion.Item>
</Accordion.Root>;
Examples
Open multiple panels
You can set up the accordion to allow multiple panels to be open at the same time using the multiple prop.
import type { JSX } from '@solidjs/web';
import { Accordion } from 'base-ui-solid/accordion';
import styles from './index.module.css';
export default function ExampleAccordion() {
return (
<Accordion.Root class={styles.Accordion} multiple>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
What is Base UI?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Base UI is a library of high-quality unstyled React components for design systems and
web apps.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
How do I get started?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Head to the “Quick start” guide in the docs. If you’ve used unstyled libraries before,
you’ll feel at home.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
Can I use it for my project?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>Of course! Base UI is free and open source.</div>
</Accordion.Panel>
</Accordion.Item>
</Accordion.Root>
);
}
function PlusIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
stroke-linecap="square"
stroke-linejoin="round"
{...props}
style={
typeof props.style === 'string'
? `display: block; ${props.style}`
: { display: 'block', ...(typeof props.style === 'object' ? props.style : {}) }
}
>
<path d="M1.5 8h13M8 14.5v-13" />
</svg>
);
}
Hidden until found
The hiddenUntilFound prop hides closed panels with hidden="until-found" so the browser can search their contents and reveal the matching panel automatically. It can be set on each Accordion.Panel, or once on Accordion.Root to apply to all panels.
To try it, press Ctrl+F (Cmd+F on macOS) and search for “restocking”—the browser opens the closed panel containing the match. When hiddenUntilFound is enabled, closed panels always remain mounted in the DOM, which also makes their contents indexable by search engines.
Older browsers that don’t support hidden="until-found" keep panels hidden until their trigger opens them, and find-in-page skips over the contents.
import type { JSX } from '@solidjs/web';
import { Accordion } from 'base-ui-solid/accordion';
import styles from './index.module.css';
export default function ExampleAccordion() {
return (
<Accordion.Root class={styles.Accordion} hiddenUntilFound>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
How long does shipping take?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Standard shipping takes 3–5 business days. Express delivery arrives in 1–2 business
days.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
What is your return policy?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
You can return any item within 30 days of delivery. Opened items may be subject to a 10%
restocking fee.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
Do you ship internationally?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Yes, we ship to over 40 countries. International orders typically arrive within 7–14
business days.
</div>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item class={styles.Item}>
<Accordion.Header class={styles.Header}>
<Accordion.Trigger class={styles.Trigger}>
How can I track my order?
<PlusIcon class={styles.Icon} />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Panel class={styles.Panel}>
<div class={styles.Content}>
Once your order ships, you’ll receive a tracking link by email. Tracking updates can
take up to 24 hours to appear.
</div>
</Accordion.Panel>
</Accordion.Item>
</Accordion.Root>
);
}
function PlusIcon(props: JSX.IntrinsicElements['svg']) {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
stroke-linecap="square"
stroke-linejoin="round"
{...props}
style={
typeof props.style === 'string'
? `display: block; ${props.style}`
: { display: 'block', ...(typeof props.style === 'object' ? props.style : {}) }
}
>
<path d="M1.5 8h13M8 14.5v-13" />
</svg>
);
}
API reference
Root
Groups all parts of the accordion.
Renders a <div> element.
defaultValueValue[]—
value prop instead.Value[]valueValue[]—
defaultValue prop instead.Value[]onValueChangefunction—
((value: Value[], eventDetails: Accordion.Root.ChangeEventDetails) => void)hiddenUntilFoundbooleanfalse
keepMounted prop and uses hidden="until-found"
to hide the element without removing it from the DOM.booleanloopFocusboolean—
booleanmultiplebooleanfalse
booleandisabledbooleanfalse
booleanorientationOrientation'vertical'
Orientationclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)keepMountedbooleanfalse
hiddenUntilFound is used.booleanrenderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-orientation-—
-data-disabled-—
-Attribute | Description | |
|---|---|---|
data-orientation | Indicates the orientation of the accordion. | |
data-disabled | Present when the accordion is disabled. | |
Root.State
type AccordionRootState<TValue = any> = {
/**
* The current value.
* Treat it as read-only: it may be a shared frozen array when no value is set.
*/
value: TValue[];
/** Whether the component should ignore user interaction. */
disabled: boolean;
/**
* The component orientation.
*
* Deprecated following the [APG guidance update](https://github.com/w3c/aria-practices/pull/3434)
* to remove roving focus.
*
* This state no longer affects keyboard focus behavior.
* @deprecated
*/
orientation: Orientation;
};Root.ChangeEventReason
type AccordionRootChangeEventReason = 'trigger-press' | 'none';Root.ChangeEventDetails
type AccordionRootChangeEventDetails = (
| { reason: 'trigger-press'; event: MouseEvent | PointerEvent | TouchEvent | KeyboardEvent }
| { reason: 'none'; event: Event }
) & {
/** Cancels Base UI from handling the event. */
cancel: () => void;
/** Allows the event to propagate in cases where Base UI will stop the propagation. */
allowPropagation: () => void;
/** Indicates whether the event has been canceled. */
isCanceled: boolean;
/** Indicates whether the event is allowed to propagate. */
isPropagationAllowed: boolean;
/** The element that triggered the event, if applicable. */
trigger: Element | undefined;
};Root.Value
type AccordionRootValue<TValue = any> = TValue[];Item
Groups an accordion header with the corresponding panel.
Renders a <div> element.
valueany—
anyonOpenChangefunction—
((open: boolean, eventDetails: Accordion.Item.ChangeEventDetails) => void)disabledbooleanfalse
booleanclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-open-—
-data-disabled-—
-data-indexnumber—
numberAttribute | Description | |
|---|---|---|
data-open | Present when the accordion item is open. | |
data-disabled | Present when the accordion item is disabled. | |
data-index | Indicates the index of the accordion item. | |
Item.State
type AccordionItemState = {
/** Whether the accordion item's panel is currently hidden. */
hidden: boolean;
/** The item index. */
index: number;
/** Whether the component is open. */
open: boolean;
/**
* The current value.
* Treat it as read-only: it may be a shared frozen array when no value is set.
*/
value: any[];
/** Whether the component should ignore user interaction. */
disabled: boolean;
/**
* The component orientation.
*
* Deprecated following the [APG guidance update](https://github.com/w3c/aria-practices/pull/3434)
* to remove roving focus.
*
* This state no longer affects keyboard focus behavior.
* @deprecated
*/
orientation: Orientation;
};Item.ChangeEventReason
type AccordionItemChangeEventReason = 'trigger-press' | 'none';Item.ChangeEventDetails
type AccordionItemChangeEventDetails = (
| { reason: 'trigger-press'; event: MouseEvent | PointerEvent | TouchEvent | KeyboardEvent }
| { reason: 'none'; event: Event }
) & {
/** Cancels Base UI from handling the event. */
cancel: () => void;
/** Allows the event to propagate in cases where Base UI will stop the propagation. */
allowPropagation: () => void;
/** Indicates whether the event has been canceled. */
isCanceled: boolean;
/** Indicates whether the event is allowed to propagate. */
isPropagationAllowed: boolean;
/** The element that triggered the event, if applicable. */
trigger: Element | undefined;
};Header
A heading that labels the corresponding panel.
Renders an <h3> element.
classfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-open-—
-data-disabled-—
-data-indexnumber—
numberAttribute | Description | |
|---|---|---|
data-open | Present when the accordion item is open. | |
data-disabled | Present when the accordion item is disabled. | |
data-index | Indicates the index of the accordion item. | |
Header.State
type AccordionHeaderState = {
/** Whether the accordion item's panel is currently hidden. */
hidden: boolean;
/** The item index. */
index: number;
/** Whether the component is open. */
open: boolean;
/**
* The current value.
* Treat it as read-only: it may be a shared frozen array when no value is set.
*/
value: any[];
/** Whether the component should ignore user interaction. */
disabled: boolean;
/**
* The component orientation.
*
* Deprecated following the [APG guidance update](https://github.com/w3c/aria-practices/pull/3434)
* to remove roving focus.
*
* This state no longer affects keyboard focus behavior.
* @deprecated
*/
orientation: Orientation;
};Trigger
A button that opens and closes the corresponding panel.
Renders a <button> element.
nativeButtonbooleantrue
<button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).booleanclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)renderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-panel-open-—
-data-disabled-—
-data-indexnumber—
numberAttribute | Description | |
|---|---|---|
data-panel-open | Present when the accordion panel is open. | |
data-disabled | Present when the accordion item is disabled. | |
data-index | Indicates the index of the accordion item. | |
Trigger.State
type AccordionTriggerState = {
/** Whether the accordion item's panel is currently hidden. */
hidden: boolean;
/** The item index. */
index: number;
/** Whether the component is open. */
open: boolean;
/**
* The current value.
* Treat it as read-only: it may be a shared frozen array when no value is set.
*/
value: any[];
/** Whether the component should ignore user interaction. */
disabled: boolean;
/**
* The component orientation.
*
* Deprecated following the [APG guidance update](https://github.com/w3c/aria-practices/pull/3434)
* to remove roving focus.
*
* This state no longer affects keyboard focus behavior.
* @deprecated
*/
orientation: Orientation;
};Panel
A collapsible panel with the accordion item contents.
Renders a <div> element.
hiddenUntilFoundbooleanfalse
keepMounted prop and uses hidden="until-found"
to hide the element without removing it from the DOM.booleanclassfunction—
JSX.ClassValue | ((state) => JSX.ClassValue)stylefunction—
JSX.CSSProperties | string | ((state) => JSX.CSSProperties | string | undefined)keepMountedbooleanfalse
hiddenUntilFound is used.booleanrenderfunction—
keyof JSX.IntrinsicElements | Component | ((props, state) => JSX.Element)Data attributes
data-open-—
-data-orientation-—
-data-disabled-—
-data-indexnumber—
numberdata-starting-style-—
-data-ending-style-—
-Attribute | Description | |
|---|---|---|
data-open | Present when the accordion panel is open. | |
data-orientation | Indicates the orientation of the accordion. | |
data-disabled | Present when the accordion item is disabled. | |
data-index | Indicates the index of the accordion item. | |
data-starting-style | Present when the panel begins animating in. | |
data-ending-style | Present when the panel is animating out. | |
CSS variables
--accordion-panel-heightnumber—
number--accordion-panel-widthnumber—
numberCSS Variable | Description | |
|---|---|---|
--accordion-panel-height | The accordion panel’s height. | |
--accordion-panel-width | The accordion panel’s width. | |
Panel.State
type AccordionPanelState = {
/** The transition status of the component. */
transitionStatus: TransitionStatus;
/** Whether the accordion item's panel is currently hidden. */
hidden: boolean;
/** The item index. */
index: number;
/** Whether the component is open. */
open: boolean;
/**
* The current value.
* Treat it as read-only: it may be a shared frozen array when no value is set.
*/
value: any[];
/** Whether the component should ignore user interaction. */
disabled: boolean;
/**
* The component orientation.
*
* Deprecated following the [APG guidance update](https://github.com/w3c/aria-practices/pull/3434)
* to remove roving focus.
*
* This state no longer affects keyboard focus behavior.
* @deprecated
*/
orientation: Orientation;
};