Skip to content
TopClass UI

Accordion

Sections a reader opens one at a time. Closed panels stay in the HTML, so a crawler reads them and find-in-page opens them.

split - size not measured yet

Accordion

split - size not measured yet

Sections a reader opens one at a time. Closed panels stay in the HTML, so a crawler reads them and find-in-page opens them.

Install

Try it

hiddenUntilFound
headingLevel

The code

accordion.tsx
import { AccordionItem, AccordionRoot } from 'topclassui/accordion'

<AccordionRoot className="max-w-105">
  <AccordionItem value="server" title="What is a server component?">
    <p>A component that renders on the server and sends HTML, with no JavaScript of its own.</p>
  </AccordionItem>
  <AccordionItem value="arabic" title="Does it work in Arabic?">
    <p>Yes. The caret moves to the left edge and the text starts at the right one.</p>
  </AccordionItem>
  <AccordionItem value="bundle" title="How big is the bundle?">
    <p>About one kilobyte in the browser. The answers cost nothing: they are plain HTML.</p>
  </AccordionItem>
</AccordionRoot>

A closed panel is hidden="until-found": the browser's search, an #id link or a text fragment opens it.

Every state

2 states

The panel of an open item is shown under its trigger.

Import from the package

topclassui/accordion
import { AccordionRoot, AccordionItem } from 'topclassui/accordion'

Install once:npm i topclassui@beta zod

Examples

  • Two flat parts and no bare Accordion: AccordionRoot and AccordionItem. An item needs value and title; its children are the panel.
  • Open at first: defaultValue={['install']} on the root, the value of each item that starts open. It is plain data, so a server page can pass it.
  • One at a time is the default, and pressing the open item closes it. multiple leaves the others as they are.
  • The title is text or inline elements, such as an icon or a Badge. No link and no button: it sits inside a button. Put the link in the panel.
  • A table wider than the box scrolls sideways inside its panel. A long title wraps and is never cut.
  • hiddenUntilFound={false} on the root takes closed panels out of the page; keepMounted then keeps them, hidden from the search too.
  • From a server page, pass text, booleans and defaultValue. value with onValueChange, and onOpenChange, need a 'use client' file.
basic

first-open

Run npx topclassui add accordion. The files land in your own repository.

multiple

Team, billed every month.

Twelve of twenty are in use.

disabled

long-content

See every order

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Three invoices arrived since your last visit.

many-items

In Arabic, right to left

مكوّن يُعرض على الخادم ويرسل HTML فقط، دون أي JavaScript خاص به.

Props

AccordionRoot

The props of AccordionRoot
PropTypeDefaultLimitsDescription
hiddenUntilFoundbooleantrue-AccordionRoot: closed panels stay in the HTML, where find-in-page and a crawler read them.

AccordionItem

The props of AccordionItem
PropTypeDefaultLimitsDescription
title*ReactNode--The text of the trigger. It sits inside a button: text and inline elements, never a link or a control.
value*string--The name of this item in the root's value and defaultValue.
childrenReactNode--What the panel holds. It stays in the HTML while the panel is closed.
headingLevelinteger, min 2, max 63min 2, max 6AccordionItem: the level of the heading its trigger sits in, clamped to h2 through h6.

Keyboard and accessibility

The keyboard of Accordion
KeysWhat it does
TabMoves from trigger to trigger.
ShiftTabMoves back.
EnterOpens or closes the focused item.
SpaceOpens or closes the focused item.
  • Set headingLevel on each item, 2 to 6 (default 3): one more than the heading above the accordion. It is clamped to that range.
  • A screen reader hears the trigger as a button in a heading, expanded or collapsed, and an open panel as a region named by its title.
  • A disabled item keeps its trigger in the tab order and says that it is disabled. Reduced motion removes the 150 ms fade.

Right to left

Nothing is told the direction: under dir="rtl" the title starts at the right and the caret sits at the left, the inline end.

Server and client

Renders on the server; the interactive part is a client component that ships only when it is used.

Files: index.tsx, client.tsx.

On the chip: split - size not measured yet.