Skip to the content

Kongens Nytorv

Metro M1, M2 and M3.

The shape says how sure we are about where a vehicle is. A solid dot is live, a fainter dot is ageing, a ring is estimated and a dashed ring is timetable only.

Props

NameTypeDefaultDescription
valuestring[]—The values of the open items, when the caller controls them. [] opens none.
defaultValuestring[][]The values of the items open at first, when Accordion keeps them itself. Left out, every item starts closed.
onChange(event: Event, value: string[], details: Accordion.Root.ChangeEventDetails) => void—Called when an item opens or closes, with the values of the items open after it: by a press, Enter or Space on a trigger (details.reason 'trigger-press'), or by the browser's find in page with hiddenUntilFound ('none'). event is details.event.
childrenReactNode—The AccordionItems, in reading order.
classNamestring—Reaches the root. className is merged after pn-Accordion.
styleCSSProperties—Reaches the root. className is merged after pn-Accordion.
refRef<HTMLDivElement>—Reaches the root. className is merged after pn-Accordion.
valuestring[], default —
The values of the open items, when the caller controls them. [] opens none.
defaultValuestring[], default []
The values of the items open at first, when Accordion keeps them itself. Left out, every item starts closed.
onChange(event: Event, value: string[], details: Accordion.Root.ChangeEventDetails) => void, default —
Called when an item opens or closes, with the values of the items open after it: by a press, Enter or Space on a trigger (details.reason 'trigger-press'), or by the browser's find in page with hiddenUntilFound ('none'). event is details.event.
childrenReactNode, default —
The AccordionItems, in reading order.
classNamestring, default —
Reaches the root. className is merged after pn-Accordion.
styleCSSProperties, default —
Reaches the root. className is merged after pn-Accordion.
refRef<HTMLDivElement>, default —
Reaches the root. className is merged after pn-Accordion.

Use it

import { Accordion } from '@mylder/perron'

Ask your agent

get_reference({ library: "base-ui", component: "Accordion" })

Keyboard and ARIA

From the Base UI reference for Accordion and the installed AccordionRoot, AccordionItem, AccordionTrigger and AccordionPanel. "From" says who does it.

KeyDoesFrom
TabMoves focus to the next trigger, or into an open panel's first focusable element, in document order; skips a disabled triggerThe browser
Shift+TabMoves focus back the same wayThe browser
Enter, SpaceOpen the focused trigger's panel, or close it while open; with multiple off, opening one closes the otherThe browser's click; Base UI for a non-button render

The arrow keys, Home and End do nothing: Base UI 1.9.0 follows the APG's update to the accordion pattern, which drops the roving focus, and the component adds no keys of its own.

ElementRoleAccessible name fromStates announced
rootnone: a <div>nonenone
itemnone: a <div>nonenone
headerheading, level 3, or the level slotProps.header.render givesIts text, the trigger'snone
triggerbutton, from the native <button>Its text content: the title; the chevron is aria-hiddenaria-expanded; aria-controls while open; disabled by the native disabled
chevronnone: hidden from screen readersnonenone
panelregion, from Base UIIts trigger, through aria-labelledby, which Base UI setsnone

What axe won't catch, from the contract, each with how this component meets it:

  • Everything the eye is told, the ear is told, for every shape each prop accepts: whether a panel is open, which the chevron shows, is the trigger's aria-expanded; a disabled trigger's tertiary colour is the native disabled; the chevron itself is aria-hidden.
  • Every landmark and named region has an accessible name: each open panel is a region named by its trigger through Base UI's aria-labelledby, so its name is the item's title, which is required.
  • Every default label can be overridden: none here; Accordion owns no strings.
  • Every shape the type accepts renders, in every variant: title renders a string, an element and a list alike, as phrasing content in the button; children renders a string, an element and a list alike in the panel's <div>.
  • A hidden copy kept for animation is aria-hidden, and a collapsed region is inert: a closed panel is unmounted, or with keepMounted or hiddenUntilFound kept hidden, which takes it out of the accessibility tree and the Tab order as inert would; Accordion keeps no copy for animation: a closing panel is the panel itself, shrinking for duration.switch under a trigger that already says aria-expanded="false", then unmounted or hidden by Base UI.
  • Native semantics first: a native <h3> holding a native <button>, with Base UI's aria-expanded, aria-controls and the panel's region named by aria-labelledby; no ARIA from the caller.
  • Focus moves in reading order and returns on close: Tab goes from a trigger into its open panel and on to the next trigger, in document order; closing a panel leaves focus on the trigger that closed it.
  • Every target is at least size.target-min square: the smallest target is a trigger with a one-word title, size.button (36 px) tall and the accordion's full width.
  • Loops stop when the system asks for less motion: no loop; the panel's height and the chevron's turn are transitions over duration.switch, which the tokens shorten to 0.01 ms under reduced motion, so both change at once with no query in the component (CR-10).

Owned strings

No owned strings: every word comes from the caller, as an item's title and its children.

Do and don't

For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.

RuleLevelDoDon'tChecked by
DD-1shouldUse Accordion for sections a person reads one or two of, such as help questions or the details of each planned workHide what most people need behind a heading, such as a stop's next departures: show it, under a Sectionreview
DD-2shouldUse Tabs to switch between views of one thingUse Accordion as tabs stacked down the page: an accordion's sections are read in place, one under anotherreview
DD-3mustWrite each title as a few words or a short question in sentence case, saying what the panel holds: "What the dot shapes mean"Write "Read more" or "Details" on every item: each trigger and its region are named by the title, so every one would sound the samereview
DD-4mustKeep a title to words: an icon or a badge beside it is fine, a link or a button is notPut a link or a button in a title: it renders inside the trigger's <button>, where nothing else can be pressedreview
DD-5mustGive items a value when the accordion opens one at first or the caller controls itRead a generated value from onChange and store it: it changes from one mount to the nextreview
DD-6shouldSet multiple when people compare items side by sideLeave items that are read together closing each otherreview
DD-7mustSet the heading level with slotProps.header.render (render: <h4 />) when the accordion sits under an h3; under a panel's or a dialog's h2, keep the h3Leave the outline skipping a level, or wrap the title in a heading of your own inside the buttonreview
DD-8shouldUse hiddenUntilFound on long help pages, so the browser's find in page reaches closed answersUse keepMounted to make closed content searchable: a hidden panel is out of find in page as wellreview
DD-9mustLeave out an item whose panel would have nothing in it, or open it to an EmptyState that says whyDisable an item because its panel has nothing to show: a disabled trigger can't say why, and it leaves the Tab orderreview