Skip to content
kerfjsGitHub

Accordion

A stack of headings that open to show more.

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { Accordion } from '@kerf-js/veneer'export function Boxed() {  return (  <Accordion    defaultOpen={['rollback']}    items={QUESTIONS}  />  )}const QUESTIONS = [  {    value: 'rollback',    title: 'How do I roll back a deploy?',    content:      'Open the deploy you want and press ' +      'Promote. Traffic moves over in about ten seconds.',  },  {    value: 'domains',    title: 'Can I bring my own domain?',    content:      'Yes. Add it under Settings, then ' +      'point a CNAME at kerf.app.',  },  {    value: 'bill',    title: 'What counts towards my bill?',    content:      'Requests and build minutes. Preview ' +      'deploys are free.',  },]

Installation

npm install @kerf-js/veneer

Try it

Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.

Live preview
Controls
Variant
Codeupdates as you change the controls
import { Accordion } from '@kerf-js/veneer'<Accordion defaultOpen={['rollback']} items={QUESTIONS} />

Usage

tsx
import { Accordion } from '@kerf-js/veneer'<Accordion  defaultOpen={['rollback']}  items={QUESTIONS}/>

Line

Hairlines only, for inside a card.

tsx
import { Accordion } from '@kerf-js/veneer'export function Line() {  return (  <Accordion    variant="line"    defaultOpen={['rollback']}    items={QUESTIONS}  />  )}const QUESTIONS = [  {    value: 'rollback',    title: 'How do I roll back a deploy?',    content:      'Open the deploy you want and press ' +      'Promote. Traffic moves over in about ten seconds.',  },  {    value: 'domains',    title: 'Can I bring my own domain?',    content:      'Yes. Add it under Settings, then ' +      'point a CNAME at kerf.app.',  },  {    value: 'bill',    title: 'What counts towards my bill?',    content:      'Requests and build minutes. Preview ' +      'deploys are free.',  },]

Several open

multiple leaves the others alone.

tsx
import { Accordion } from '@kerf-js/veneer'export function Multiple() {  return (  <Accordion    multiple    defaultOpen={['rollback', 'bill']}    items={QUESTIONS}  />  )}const QUESTIONS = [  {    value: 'rollback',    title: 'How do I roll back a deploy?',    content:      'Open the deploy you want and press ' +      'Promote. Traffic moves over in about ten seconds.',  },  {    value: 'domains',    title: 'Can I bring my own domain?',    content:      'Yes. Add it under Settings, then ' +      'point a CNAME at kerf.app.',  },  {    value: 'bill',    title: 'What counts towards my bill?',    content:      'Requests and build minutes. Preview ' +      'deploys are free.',  },]

A disabled item

Set disabled on the item, and say why in its title.

tsx
import { Accordion } from '@kerf-js/veneer'export function Disabled() {  return (  <Accordion    items={[      {        value: 'plan',        title: 'Plan',        content: 'Team, billed monthly.',      },      {        value: 'seats',        title: 'Seats',        content: 'Eight of ten in use.',      },      {        value: 'sso',        title: 'Single sign-on (Enterprise)',        content: '',        disabled: true,      },    ]}  />  )}

Hearing what is open

onChange gets the open values each time.

tsx
import { state } from '@kerf-js/core'import { Accordion, Stack, Text } from '@kerf-js/veneer'export function HeardChange() {  const open = state(['rollback'])  return (    <Stack gap="group">      <Accordion        multiple        defaultOpen={['rollback']}        onChange={(list) => (open.value = list)}        items={QUESTIONS}      />      <Text subtle>        {open.value.length} of {QUESTIONS.length} open      </Text>    </Stack>  )}const QUESTIONS = [  {    value: 'rollback',    title: 'How do I roll back a deploy?',    content:      'Open the deploy you want and press ' +      'Promote. Traffic moves over in about ten seconds.',  },  {    value: 'domains',    title: 'Can I bring my own domain?',    content:      'Yes. Add it under Settings, then ' +      'point a CNAME at kerf.app.',  },  {    value: 'bill',    title: 'What counts towards my bill?',    content:      'Requests and build minutes. Preview ' +      'deploys are free.',  },]

Project settings

An item's content can be any JSX.

tsx
import {  Accordion,  Input,  Stack,  Switch} from '@kerf-js/veneer'export function Settings() {  return (  <Accordion    variant="line"    defaultOpen={['general']}    items={[      {        value: 'general',        title: 'General',        content: (          <Input            label="Project name"            defaultValue="storefront"          />        ),      },      {        value: 'build',        title: 'Build',        content: (          <Stack gap="related">            <Input              label="Build command"              defaultValue="npm run build"            />            <Switch defaultChecked>              Cache dependencies            </Switch>          </Stack>        ),      },      {        value: 'alerts',        title: 'Alerts',        content: (          <Switch>Tell me when a deploy fails</Switch>        ),      },    ]}  />  )}

When to use

Use it for

  • Long answers where people want only one or two
  • Optional settings that can stay out of the way

Reach for something else

  • Tabs, when the sections are peers and only one is read at a time
  • Drawer, when the extra content is a task of its own

Accessibility

Enter
Opens or closes the focused item
Space
Opens or closes the focused item
↑↓
Moves between headings
HomeEnd
Jumps to the first or last heading
  • Each title is a real button inside a heading. Set level so the heading fits the page outline.

API Reference

PropTypeDefaultDescription
itemsAccordionItem[]—The entries: value, title, content and optionally disabled.
variant"line" | "boxed"'boxed'Which form it takes.
multiplebooleanfalseMore than one open at once.
defaultOpenstring[][]What starts open.
level2 | 3 | 4 | 6 | 53The heading level of each title: one below the heading the accordion sits under. Default 3, in a page section.
EventTypeDefaultDescription
onChange((open: string[]) => void)—Called with the new value.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="accordion"] [data-part="trigger"]

triggerrootitemheadingchevroncontentsweep

RelatedTabsCardDrawer