---
title: "Accordion"
description: "Disclosure panels with AccordionTitle and AccordionContent, token variants, initial state and ARIA wiring."
requested_language: cs
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/cs/components/accordion
version: unreleased (main)
docs_index: https://docs.systhema.app/cs/llms.txt
---
> This page isn't translated yet. Showing English.


An accordion hides a block of content behind a title the visitor clicks to expand or collapse. Use a group of them for FAQs, specifications or any long content a reader skims for one answer.

```tsx preview title="Accordion"
import { Accordion, AccordionContent, AccordionTitle, Paragraph, Stack } from '@systhemaui/next'

export default function Demo() {
  return (
    <Stack direction="col" gap="sm" style={{ width: '100%', maxWidth: 560 }}>
      <Accordion>
        <AccordionTitle>What is Systhema?</AccordionTitle>
        <AccordionContent>
          <Paragraph>
            A design system framework: Figma tokens, a Tailwind CSS plugin and React components that
            read from the same source.
          </Paragraph>
        </AccordionContent>
      </Accordion>
      <Accordion>
        <AccordionTitle>Do I need Payload?</AccordionTitle>
        <AccordionContent>
          <Paragraph>No. The components work in any React or Next.js project.</Paragraph>
        </AccordionContent>
      </Accordion>
      <Accordion>
        <AccordionTitle>Can I change the icons?</AccordionTitle>
        <AccordionContent>
          <Paragraph>Yes, with the accordion options of the blocks config.</Paragraph>
        </AccordionContent>
      </Accordion>
    </Stack>
  )
}
```

## Import

```tsx
import { Accordion, AccordionContent, AccordionTitle } from '@systhemaui/next'
```

In a React app without Next.js, import them from `@systhemaui/react`.

## Variants and tags

An accordion has three parts, always nested in this order:

| Part               | Renders                                    | Role                                                                       |
| ------------------ | ------------------------------------------ | -------------------------------------------------------------------------- |
| `Accordion`        | `<div>` (`Accordion.section`: `<section>`) | Holds the open state and the ids that link the parts                       |
| `AccordionTitle`   | `<button>` (`AccordionTitle.div`: `<div>`) | The trigger; appends the open/close icon after its text                    |
| `AccordionContent` | `<div>` (`AccordionContent.div`)           | The collapsible region; wraps its children in `.accordion-content-wrapper` |

`AccordionTitle` and `AccordionContent` throw when they are rendered outside an `Accordion`.

`variant` takes an accordion variant from your tokens. The default tokens ship one, `default`, which is also what you get without the prop. Each token variant becomes one class, `accordion-<variant>`, with its own padding, border, radius, shadow, title type and colors; add variants in Figma and they appear here.

```tsx preview title="The default variant"
import { Accordion, AccordionContent, AccordionTitle, Paragraph } from '@systhemaui/next'

export default function Demo() {
  return (
    <Accordion variant="default" style={{ width: '100%', maxWidth: 560 }}>
      <AccordionTitle>Shipping and returns</AccordionTitle>
      <AccordionContent>
        <Paragraph>Orders ship within two working days. Returns are free for 30 days.</Paragraph>
      </AccordionContent>
    </Accordion>
  )
}
```

## Examples

### Expanded

`expanded` sets the state the accordion starts in. It is the initial value only: after the first render the accordion owns its state, and changing the prop does not open or close it.

```tsx preview title="Starts expanded"
import { Accordion, AccordionContent, AccordionTitle, List, ListItem } from '@systhemaui/next'

export default function Demo() {
  return (
    <Accordion expanded style={{ width: '100%', maxWidth: 560 }}>
      <AccordionTitle>What is included</AccordionTitle>
      <AccordionContent>
        <List type="check">
          <ListItem checked>Token pipeline and Tailwind CSS plugin</ListItem>
          <ListItem checked>React and Next.js components</ListItem>
          <ListItem checked>Payload blocks and editors</ListItem>
        </List>
      </AccordionContent>
    </Accordion>
  )
}
```

To re-render an accordion in a new state, change its `key` together with `expanded`.

### As a section

Pass `as="section"` (or use `Accordion.section`) when each panel is a distinct section of the page. Use `id` to fix the ids of the title and content (`accordion-title-<id>`, `accordion-content-<id>`), for example to deep-link to a panel; without it the ids come from `useId()`.

```tsx preview title="Section with a fixed id"
import { Accordion, AccordionContent, AccordionTitle, Paragraph } from '@systhemaui/next'

export default function Demo() {
  return (
    <Accordion as="section" id="opening-hours" style={{ width: '100%', maxWidth: 560 }}>
      <AccordionTitle>Opening hours</AccordionTitle>
      <AccordionContent>
        <Paragraph>Monday to Friday, 9:00 to 17:00. Closed on public holidays.</Paragraph>
      </AccordionContent>
    </Accordion>
  )
}
```

The `Accordion.div` and `Accordion.section` shorthands take only the element's attributes. For `variant`, `expanded`, `id` or `disableAnimation`, use `Accordion` with `as`.

### Rich content

`AccordionContent` takes any children. Each direct child gets the variant's content gap above it, and headings, paragraphs and lists take the content color.

```tsx preview title="Mixed content"
import { Accordion, AccordionContent, AccordionTitle, Button, Paragraph } from '@systhemaui/next'

export default function Demo() {
  return (
    <Accordion expanded style={{ width: '100%', maxWidth: 560 }}>
      <AccordionTitle>Need more help?</AccordionTitle>
      <AccordionContent>
        <Paragraph>Our support team answers within one working day.</Paragraph>
        <Paragraph type="small">Average response time last month: 3 hours.</Paragraph>
        <div>
          <Button.a href="#examples" variant="primary">
            Contact support
          </Button.a>
        </div>
      </AccordionContent>
    </Accordion>
  )
}
```

### Icons

The title icon is an empty `<span class="accordion-title-icon">` masked with an icon from the `accordion` options of [`blocks`](https://docs.systhema.app/cs/styling/blocks.md): by default a plus while closed and a cross while open. `closedIcon` and `openedIcon` take a CSS image value, typically `url("data:image/svg+xml,…")`; `openRotate` rotates the icon by that many degrees while the panel is open. `false` turns an option off.

```ts title="systhema.config.ts"
import type { SysthemaConfig } from '@systhemaui/core'

const config: SysthemaConfig = {
  blocks: {
    accordion: {
      // One chevron that turns upside down when the panel opens.
      closedIcon: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -960 960 960'%3E%3Cpath d='M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z'/%3E%3C/svg%3E")`,
      openedIcon: false,
      openRotate: 180,
    },
  },
}

export default config
```

## Props

### `Accordion`

<!-- generated:props @systhemaui/react AccordionProps -->

| Prop                        | Type                           | Default | Description |
| --------------------------- | ------------------------------ | ------- | ----------- |
| `as`                        | `ElementType`                  | `'div'` |             |
| `className`                 | `string`                       | -       |             |
| `expanded`                  | `boolean`                      | -       |             |
| `id`                        | `string`                       | -       |             |
| `variant`                   | `AccordionVariant` (`default`) | -       |             |
| `disableAnimation`          | `boolean`                      | -       |             |
| …and all `<div>` attributes |                                |         |             |

<!-- /generated -->

### `AccordionTitle`

<!-- generated:props @systhemaui/react AccordionTitleProps -->

| Prop                           | Type          | Default    | Description |
| ------------------------------ | ------------- | ---------- | ----------- |
| `as`                           | `ElementType` | `'button'` |             |
| `className`                    | `string`      | -          |             |
| …and all `<button>` attributes |               |            |             |

<!-- /generated -->

### `AccordionContent`

<!-- generated:props @systhemaui/react AccordionContentProps -->

| Prop                        | Type          | Default | Description |
| --------------------------- | ------------- | ------- | ----------- |
| `as`                        | `ElementType` | `'div'` |             |
| `className`                 | `string`      | -       |             |
| …and all `<div>` attributes |               |         |             |

<!-- /generated -->

## HTML and CSS

The open state lives in attributes: `data-expanded` on `.accordion`, `aria-expanded` on the title and `aria-hidden` on the content. The CSS collapses `.accordion-content[aria-hidden='true']` with a `grid-template-rows` transition, so the markup needs a script that flips those attributes. The [vanilla JS bundle](https://docs.systhema.app/cs/styling/vanilla-js.md) does that for every `.accordion-title` click.

```html
<div
  class="accordion accordion-default"
  data-expanded="false"
  data-title-id="faq-1-title"
  data-content-id="faq-1-content"
>
  <button
    id="faq-1-title"
    class="accordion-title"
    type="button"
    aria-expanded="false"
    aria-controls="faq-1-content"
  >
    What is Systhema?
    <span class="accordion-title-icon" aria-hidden="true"></span>
  </button>
  <div
    id="faq-1-content"
    class="accordion-content"
    aria-hidden="true"
    aria-labelledby="faq-1-title"
    tabindex="-1"
    inert
  >
    <div class="accordion-content-wrapper">
      <p class="text-body color-body">A design system framework.</p>
    </div>
  </div>
</div>
```

The variant values come from the `accordion` tokens in [spacing](https://docs.systhema.app/cs/reference/tokens/spacing.md#accordion) and [colors](https://docs.systhema.app/cs/reference/tokens/colors.md#accordion).

<!-- generated:utilities accordion -->

| Class                       | Styles                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accordion-title`           | `cursor: pointer;`<br>`width: 100%;`<br>`display: flex;`<br>`flex-direction: row;`<br>`align-items: center;`<br>`justify-content: space-between;`<br>`text-align: left;`<br>`font-family: var(--font-accordion-font-family);`<br>`font-style: var(--font-accordion-font-style);`<br>`font-weight: var(--font-accordion-font-weight);`<br>`font-variation-settings: 'wght' var(--font-accordion-font-weight);`                                                                                           |
| `accordion-title-icon`      | `& { flex-grow: 0; flex-shrink: 0; transform: rotate(0deg); transition-property: transform; }`<br>`& svg { flex-grow: 0; flex-shrink: 0; transform: rotate(0deg); transition-property: transform; }`<br>`& img { flex-grow: 0; flex-shrink: 0; transform: rotate(0deg); transition-property: transform; }`<br>`div.accordion-title-icon:empty { display: inline-block; mask-image: url("data:image/svg+xml,…"); mask-repeat: no-repeat; mask-position: center; mask-size: contain; }`<br>+ 3 more rules |
| `accordion-content`         | `& { overflow: hidden; display: grid; grid-template-rows: 1fr; transition-property: grid-template-rows; }`<br>`&[aria-hidden='true'] { grid-template-rows: 0fr; }`                                                                                                                                                                                                                                                                                                                                      |
| `accordion-content-wrapper` | `overflow: hidden;`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `accordion-default`         | `:is(.richtext > :is(.columns, .stack, .card-default, .card-highlighted, :is(.accordion-default))):not(:first-child) { margin-top: calc(var(--typography-block-margin-y, 24px) * 2); }`<br>`:is(.richtext > :is(.columns, .stack, .card-default, .card-highlighted, :is(.accordion-default))):not(:last-child) { margin-bottom: calc(var(--typography-block-margin-y, 24px) * 2); }`<br>+ 14 more rules                                                                                                 |

<!-- /generated -->

## Next.js

`@systhemaui/next` re-exports the accordion parts from `@systhemaui/react` unchanged. They are client components (`'use client'`), so you can render them from a Server Component without a wrapper.

## Accessibility

- The title is a native `<button type="button">` with `aria-expanded` and `aria-controls` pointing at the content. Rendered as any other element (`AccordionTitle.div`, or an `<a>` without `href`), it becomes a custom button: `role="button"`, `tabIndex={0}` and `Enter` / `Space` handling.
- The content has `aria-labelledby` pointing at the title. While collapsed it is `aria-hidden` and `inert`, so its links and fields leave the tab order; expanded, it is a `role="region"`.
- The icon is decorative and carries `aria-hidden="true"`.
- To expose the titles to heading navigation, place each accordion under a heading, or put the title text in one: a heading inside the button is not announced as a heading.

> [!NOTE]
> The vanilla JS bundle toggles `aria-expanded`, `aria-hidden` and `tabindex`, but not `inert`. In HTML projects, links inside a collapsed panel stay focusable.

## Related

- [Accordion block](https://docs.systhema.app/cs/payload/blocks/accordion.md)
- [Component CSS blocks](https://docs.systhema.app/cs/styling/blocks.md)
- [Accessibility](https://docs.systhema.app/cs/concepts/accessibility.md)
