Skip to content

BbAccordion

A panel that opens when you click its header, with the accessibility already wired.

import { BbAccordion } from 'bitboss-ui';

On this page

Use it for

Reach for BbAccordion when the trigger is a title sitting over the body: an FAQ entry, a settings section, a row in a list. You get the button and the accessibility for free.

Use something else when

  • BbCollapsible, if the trigger cannot be a header: a switch in a settings row, a link inside a paragraph
  • BbSmoothHeight, if you hide nothing and only want a height change to animate
  • BbTabs, if the sections are peer views of one subject and only one stays visible

Pass Through

Hover or tap a part to outline it. Toggles flip loading, errors and warnings when the component has them — only parts highlight.

The API is the same as v2. Nothing here needs migrating.

Default

One BbAccordion is one panel, and one panel is one boolean.

Orders placed before 15:00 CET ship the same day. Delivery inside the EU takes 2 to 4 working days and is tracked from the moment it leaves the warehouse.

The header slot lands inside a real <button>, already carrying aria-expanded and aria-controls. The body is the region that button labels. You add none of it, and the keyboard comes with the button.

Never put a <button> or an <a> inside header. Actions belong in the body.

A list of panels

There is no BbAccordionGroup. Render several panels, one boolean each.

Yes. New seats are billed pro rata for the days left in the current cycle, and the full amount from the next invoice.

The header spans the panel and is the only click target. Whatever you slot into it becomes click-transparent, so a badge, an icon or a <span> never swallows the toggle. A :hover on those children never fires, though: hang it on the header.

Panels nest with no extra wiring. Pad the nested body, or the hierarchy disappears.

One panel at a time

Track one active key and derive every panel from it.

  • Unlimited workspaces
  • SAML and OIDC
  • Priority support

Clicking the open header emits false. Reset the key to null in that branch, or the panel shuts visually while the key still claims it is open.

Use a stable key rather than an index when the list can reorder or filter. And do not write a wrapper that forces siblings closed.

The slots

header and the body receive the same two things, value and toggle.

Lumen Sit-Stand Desk 160× 1
Northlight 27" 4K Display× 1

value says whether the panel is open. Use it to rotate a chevron or swap the header copy. Assigning to it does nothing.

toggle() earns its place in the body, as a "Show less" at the end of a long panel. In the header it is redundant, since the button already toggles.

Listen to update:modelValue only for side effects: analytics, a fetch on first open. The open or close has already happened.

When the body renders

The body does not exist until the first open, and it stays mounted from then on.

Open the panel, clear the purchase order, close it again — Save is still blocked.

Add eager when the content has to exist while the panel is shut. A validated field takes part in the form only once it has rendered. Without eager the form submits as though the field were not there, and it starts blocking submission the first time somebody opens the panel. The same goes for text a prerendered page has to carry.

If a collapsed panel can hold an invalid field, open it when validation fails.

Closed content is aria-hidden and inert, eager or not: out of the tab order, unclickable, invisible to screen readers. The mechanism is documented on BbCollapsible.

Pass id when the markup has to be predictable. The body takes your id and the header takes <id>_header, already cross-wired.

Spacing and styling

Put padding and borders on the content you slot in, never on the body.

The body zeroes padding, borders and gaps in both states, and the header button carries none of its own. That is why every demo above pads the <span> inside the header and the element inside the body.

transition-duration (milliseconds, 250 by default) drives the height and the fade together. Under prefers-reduced-motion: reduce both drop to near-instant on their own.

Three classes are yours: .bb-accordion on the panel, .bb-accordion__header on the button, .bb-accordion__content on the body. If you repaint the header, keep a visible focus ring: the browser supplies none.

In a list, the borders and dividers belong to the container that renders the panels. That is what keeps a collapsed panel from leaving a stray line.