Accordion

Accordion shows and hides sections of content under clickable headers. Use it for study-guide sections, FAQs and groups of settings.

  • Beta
  • Accessibility
  • Figma none (C3)
  • packages/ui/src/components/Accordion

Beta until Figma has an Accordion component (C3). The card style follows the live nuton.app FAQ; the flush style, chevron, icon and description are code-only. Everything on this page is implemented and tested.

Preview

Change the controls to try every supported combination. The preview renders the real component and the code updates with it.

A topic you type, or your own PDFs, slide decks, YouTube videos, audio, web pages and notes.

Only you, unless you share a space. Your uploads stay in your own spaces.

Each lesson ends with flashcards and a short quiz. Nuton brings back the cards you found hardest.
tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@nuton/ui'

<Accordion defaultValue="sources">
  <AccordionItem value="sources">
    <AccordionTrigger>What can I turn into a course?</AccordionTrigger>
    <AccordionContent>A topic you type, or your own PDFs, slide decks, YouTube videos, audio, web pages and notes.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="privacy">
    <AccordionTrigger>Who can see my study materials?</AccordionTrigger>
    <AccordionContent>Only you, unless you share a space. Your uploads stay in your own spaces.</AccordionContent>
  </AccordionItem>
  <AccordionItem value="revision">
    <AccordionTrigger>How does revision work?</AccordionTrigger>
    <AccordionContent>Each lesson ends with flashcards and a short quiz. Nuton brings back the cards you found hardest.</AccordionContent>
  </AccordionItem>
</Accordion>

Installation

Availability

@nuton/ui and @nuton/tokens are private workspace packages. They aren't published to a registry yet (D4: registry deferred; D12: distribution waits on the provenance review). Inside the Nuton monorepo, add them as workspace dependencies. The commands below apply once the packages are published.

1. Add the packages

bash
pnpm add @nuton/ui @nuton/tokens @remixicon/react

Peer dependencies: react and react-dom 18 or later. The chevron indicator uses @remixicon/react. Accordion is built in-house: it doesn't need @radix-ui/react-accordion.

2. Load tokens and component styles once

tsx
// app/layout.tsx (or your app's entry file)
import '@nuton/tokens/tokens.css' // --nu-* design tokens
import '@nuton/ui/styles.css'     // component styles (uses the tokens)

Closed panels are hidden by the component styles, so the stylesheet is required. Headers and panels use --nu-font-body (Inter), as described on the Button page.

Usage

tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@nuton/ui'

export function CourseFaq() {
  return (
    <Accordion defaultValue="sources">
      <AccordionItem value="sources">
        <AccordionTrigger>What can I turn into a course?</AccordionTrigger>
        <AccordionContent>A topic you type, or your own PDFs, slides, videos and notes.</AccordionContent>
      </AccordionItem>
      <AccordionItem value="privacy">
        <AccordionTrigger>Who can see my study materials?</AccordionTrigger>
        <AccordionContent>Only you, unless you share a space.</AccordionContent>
      </AccordionItem>
    </Accordion>
  )
}
  • Each AccordionItem needs a unique value. Pass defaultValue to start with an item open, or value + onValueChange to control it.
  • type="single" (default) keeps one item open, like the live FAQ. Use type="multiple" when people compare sections, such as a study guide.
  • Set headingLevel to fit the page outline. The default is 3, so an accordion under an h2 needs nothing.
  • Keep headers short and specific: they are the button's accessible name.
  • Don't hide content people must read to finish a task. Accordion is for optional or scannable detail.

Variants and sizes

Two appearances and two indicators. Each header can add an icon and a description.

Appearance

A topic you type, or your own PDFs, slide decks, YouTube videos, audio, web pages and notes.

Only you, unless you share a space. Your uploads stay in your own spaces.

A topic you type, or your own PDFs, slide decks, YouTube videos, audio, web pages and notes.

Only you, unless you share a space. Your uploads stay in your own spaces.

card is the live nuton.app FAQ: separate bordered cards, 12px apart, filled with Weak-50 when open. flush(code-only) splits rows with lines and has no side padding, for settings pages and panels that already have a frame.

Indicator

A topic you type, or your own PDFs, slide decks, YouTube videos, audio, web pages and notes.

Only you, unless you share a space. Your uploads stay in your own spaces.

The plus turns into a minus as it rotates (live FAQ). The chevron (code-only) turns over. Both are decorative: aria-expanded carries the state.

Icon and description

What design is for, and the principles you will meet in every chapter.

How an object’s shape tells people what it does.

Research, personas and testing with real people.

The icon is hidden from screen readers. The description is part of the button's name, read after the title, so the name matches the visible text (WCAG 2.5.3); the panel region is named by the title alone. The panel text lines up with the title.

Dimensions

PartSizeSpacingType
Card header56px tall for one line (1 + 13 + 28 + 13 + 1)Padding 14 / 20 (inside a 1px border); 10px between icon, text and indicatorTitle Label/Medium 16/28 Strong-950
Card panelContent height10px under the title; 14px bottom; aligned with the titleParagraph/Small 14/24 Sub-600
Card stackFull width12px between cards; radius 16 (live)—
Flush row56px plus a 1px linePadding 14 / 0As card
Icon / indicator20px / 24px box (plus glyph 12 × 2; chevron 20)Centred on the 28px title line—
Description—Under the titleParagraph/X Small 12/16 Sub-600

Taken from the component CSS. Radius 16 matches the live FAQ (owner decision, 2026-10-14).

States and interactions

Each header is a button, so it has the usual button states. The open state animates the panel's height.

Text
Closedaria-expanded="false". The panel is hidden and inert.

The answer shows here.
OpenCard: border clears, fill Weak-50; the plus becomes a minus.

Text
DisabledNative disabled: skipped by Tab and arrow keys; text Disabled-500.
Tab to a header
Hover and focusHover fills the card with Weak-50. Keyboard focus draws the primary border and focus halo round the whole card (as Card); flush rows get a 2px ring.

This one can't close until another opens.
Locked opencollapsible={false}: the open header is aria-disabled until another item opens.

What design is for, and the principles you will meet in every chapter.

How an object’s shape tells people what it does.

Research, personas and testing with real people.

1 of 3 sections open

ControlledExpand or collapse every section from outside with value.

The height, fill and indicator animate over --nu-duration-slow (300ms). Under reduced motion they change at once.

Tokens

TokenDefaultUsed for
--nu-accordion-borderStroke/Soft-200Card border, flush lines
--nu-accordion-bg-hover · --nu-accordion-bg-openBg/Weak-50Card hover and open fill
--nu-accordion-fg · --nu-accordion-fg-subText/Strong-950 · Text/Sub-600Title · panel and description
--nu-accordion-marker · --nu-accordion-iconIcon/Strong-950 · Icon/Sub-600Plus · icon and chevron
--nu-accordion-focus · --nu-accordion-focus-shadowState/Primary/Base · buttons/primary-focusCard focus
--nu-accordion-radius · --nu-accordion-gap12px · 12pxCard radius · space between cards
--nu-accordion-duration--nu-duration-slowAll transitions

Composition examples

FAQ on a help page

One question open at a time, as on nuton.app. The page heading is an h2, so the questions stay h3.

A topic you type, or your own PDFs, slide decks, YouTube videos, audio, web pages and notes.

Only you, unless you share a space. Your uploads stay in your own spaces.

Each lesson ends with flashcards and a short quiz. Nuton brings back the cards you found hardest.

Groups of settings

Flush rows with icons and a summary line. Several groups can be open; controls inside work as usual.

Study guide sections

Use type="multiple" so students can keep several chapters open. Pair it with an Expand all button when there are many sections (see Controlled).

API reference

Accordion props

Accordion props
PropTypeDefaultDescription
type'single' | 'multiple''single'single: one item open at a time. multiple: any number.
valuestring | null (single) · string[] (multiple)—Open item(s), controlled. Use with onValueChange.
defaultValuestring | null (single) · string[] (multiple)—Open item(s) on first render, uncontrolled.
onValueChange(value: string | null) => void · (value: string[]) => void—Called when an item opens or closes. Single gets null when the open item closes.
collapsiblebooleantrueSingle only. false keeps one item open: the open header becomes aria-disabled.
appearance'card' | 'flush''card'card: bordered cards (live FAQ). flush: rows split by lines (code-only).
indicator'plus' | 'chevron''plus'Marker at the end of each header.
headingLevel2 | 3 | 4 | 5 | 63Heading level that wraps every header button.
disabledbooleanfalseDisables every item.
refRef<HTMLDivElement>—Forwarded to the root.
…restHTMLAttributes<HTMLDivElement>—Passed to the root (className, id, aria-*…).

AccordionItem props

AccordionItem props
PropTypeDefaultDescription
valuerequiredstring—Unique key for this item.
disabledbooleanfalseDisables this item’s header.
refRef<HTMLDivElement>—Forwarded to the item.
…restHTMLAttributes<HTMLDivElement>—Passed to the item.

AccordionTrigger props

AccordionTrigger props
PropTypeDefaultDescription
childrenrequiredReactNode—Header text and the button’s accessible name.
descriptionReactNode—Line under the title. Read after the title as part of the button's name.
iconReactNode—Decorative 20px icon before the title.
headingLevel2 | 3 | 4 | 5 | 6—Overrides the Accordion's headingLevel for this header.
onClickMouseEventHandler—Runs first; call event.preventDefault() to stop the toggle.
refRef<HTMLButtonElement>—Forwarded to the button.
…restHTMLAttributes<HTMLButtonElement>—Passed to the button.

AccordionContent props

AccordionContent props
PropTypeDefaultDescription
childrenReactNode—Panel content. It stays mounted when closed.
refRef<HTMLDivElement>—Forwarded to the region.
…restHTMLAttributes<HTMLDivElement>—Passed to the region.

Styling hooks

The root is .nu-accordion with data-appearance, data-indicator and data-type. Items, buttons and panels carry data-state="open|closed". Use the tokens in Tokens to adjust it.

Accessibility

Keyboard

KeyBehaviour
TabMoves to the next header, then into an open panel’s content.
Enter / SpaceOpens or closes the focused header’s panel.
↓ / ↑Moves to the next or previous header, wrapping round. Disabled headers are skipped.
Home / EndMoves to the first or last header.

Screen readers

WAI-ARIA accordion: each header is a button inside a heading (headingLevel), with aria-expanded and aria-controls. Each panel is a region named by its header. Closed panels are inert and visibility: hidden, so they are not read or focusable. The indicator and icon are hidden.

Focus

Focus stays on the header when it toggles. Card: primary border plus the focus halo round the whole card; flush: a 2px ring. Forced-colours mode uses Highlight.

Contrast and colour

Titles are Strong-950 (16.7:1 on Weak-50); panel text and descriptions Sub-600 (6.24:1 on Weak-50, 6.69:1 on white). The card border (Soft-200, 1.19:1) is decorative: the header text identifies each item. State is never shown by colour alone.

Touch and pointer

The whole header is the target: at least 56px tall and full width, above the 24 × 24 minimum.

You must: set headingLevel to fit the page outline; give every item a unique value; keep header text meaningful on its own (“Billing”, not “More”).

Figma mapping

Figma has no Accordion (C3; every page searched on 2026-10-14). The card follows the live nuton.app FAQ. When a Figma component is added, map its properties here and re-check the code-only choices.

Guidelines and related components

Use Accordion for

  • FAQs and help content people scan for one answer.
  • Long study guides where students open one chapter at a time.
  • Groups of settings that would make a page too long.

Don't use Accordion for

  • Switching between views of the same thing. Use Tabs.
  • Content everyone must read, such as a lesson or instructions.
  • A single “Show more”. Use a button that reveals the rest.

Do and don't

Only you.
DoWrite headers people can scan. A specific question tells people whether to open it.

Only you.
Don'tDon't use vague headers. “Details” says nothing; screen reader users hear a list of identical buttons.

What design is for, and the principles you will meet in every chapter.

How an object’s shape tells people what it does.
DoAllow several open sections when people compare. Students often keep two chapters open side by side.

Section 1…
Don'tDon't nest accordions deeply. Each level hides more; people lose track of where they are.

…
DoMatch the heading level to the page. Under an h2, items are h3 (the default); under an h3, set headingLevel={4}.

…
Don'tDon't put buttons or links in the header. The header is already a button; nested controls can't be reached reliably.

Common mistakes

  • Reusing a value in two items: both open and close together.
  • Leaving headingLevel at 3 under an h3, which breaks the page outline.
  • Loading the component without @nuton/ui/styles.css: closed panels then show.
  • Putting the only copy of required instructions in a closed panel.