Skip to content

Accordion โ€‹

A single expand/collapse unit with header (icon + title + optional subtitle + optional trailing actions) and a body. Compose multiple together for a typical FAQ or settings group. Toggle Vue / CSS to switch the live demo and source together.

The look is Basecoat's .accordion (see Theme): a p-4 row with a text-sm font-medium title and a size-4 chevron that rotates when the item opens, over a text-sm body. In a group the items flatten into one flush list with a hairline between them, exactly as upstream renders .accordion > details; on its own an item keeps this component's frame (border, radius, shadow-xs), which upstream has no equivalent for. Dark mode is built in.

Basic โ€‹

Single accordion with a title and body.

Vue SFC

Title & subtitle โ€‹

The header text comes from three places. When more than one is given, the most specific wins:

  1. slot="header" replaces the whole left side of the row: the icon, the title and the subtitle. The actions slot and the chevron stay, and the row is still the toggle.
  2. slot="title" / slot="subtitle" each replace one line.
  3. title / subtitle props are plain text.

The old prop names label / description are still accepted (as properties and as attributes) as aliases of title / subtitle. Likewise the old slot names slot="label" / slot="description" still work as aliases of slot="title" / slot="subtitle"; when both an old and a new name are given, the new one wins. The secondary line renders [mono-subtitle] (and, for existing CSS, its old name [mono-description] too).

See the Slots demo for slot="header" in action.

Sizes โ€‹

Small, medium and large.

Vue SFC

Colors โ€‹

Pick a colour and both accordions take it โ€” the accent on the active border, icon background and arrow re-paints live.

Vue SFC

States โ€‹

Default-open, default-closed and disabled.

Vue SFC

Icon & subtitle โ€‹

Leading icon plus a subtitle under the title.

Vue SFC

Group โ€‹

Multiple stacked items in the group helper โ€” one flush list with a hairline between the items, which is Basecoat's .accordion. Four of the eight flavors (maia, mira, luma, rhea) also frame the group itself; on vega it is a bare column.

Vue SFC

Slots โ€‹

Custom title, subtitle, icon, actions and body via named slots. The second item uses slot="header" to replace the title and subtitle with its own markup โ€” the icon, actions and chevron are kept.

Vue SFC

Event log โ€‹

Live log of toggle, open and close events.

Vue SFC

Customized โ€‹

Override per-element styling with cssClass (Vue) or utility classes (CSS).

Vue SFC

CSS Variables โ€‹

Vue SFC

Every accordion is themed through --mono-accordion-* custom properties. Setting one on the element, on any wrapper/ancestor, or inline all work โ€” custom properties inherit, and they pierce the shadow-DOM boundary, so the same overrides apply to <mono-accordion> and the shadow build. The size and color props set presets, but an explicit --mono-accordion-* override always wins. To re-skin globally, set the underlying tokens (--card, --border, --muted-foreground โ€ฆ) or switch flavor.

VariableDefaultControls
--mono-accordion-accent / -<role>--primary / the ten rolesThe colour the chevron, the focus ring and the open edge take (set by color)
--mono-accordion-bg (alias -surface) / -text / -border--card / --card-foreground / --borderThe item's fill, ink and edge
--mono-accordion-shadow--mono-shadow-xsThe standalone item's elevation (a grouped item has none)
--mono-accordion-open-bg (alias -head-bg) / -open-border-color / -open-shadowthe item's own surface / the role at 32% over --border / a soft role glowThe open item. Four flavors set -open-bg to bg-muted/50; vega leaves it alone
--mono-accordion-radius / -radius-<size>--mono-radius-md at mdCorner radius
--mono-accordion-pad-x-<size> / -pad-y-<size>4 ร— --mono-spacing at md (p-4)Head and body padding
--mono-accordion-gap3 ร— --mono-spacingGap between the head's parts
--mono-accordion-title-font-<size> / -title-weight / -title-hover-decoration--mono-text-sm / medium / underlineThe title. Upstream underlines it on hover โ€” set none to drop that
--mono-accordion-head-hover-tint / -head-hover-bg0% / built from itA hover fill for the row, off by default (the underline is the cue). A flavor sets the ALPHA; a consumer can set the colour outright
--mono-accordion-description-font / -description-color--mono-text-xs / --muted-foregroundThe second line (the subtitle)
--mono-accordion-body-font-<size> / -body-line-height / -body-color--mono-text-sm / its line-height / inheritThe body
--mono-accordion-arrow-size / -arrow-color / -arrow-open-colorsize-4 / --muted-foreground / the roleThe chevron
--mono-accordion-glyph-size / -glyph-radius / -glyph-bg / -glyph-color7 ร— --mono-spacing / --mono-radius-sm / the role at 14% / the roleThe leading icon chip
--mono-accordion-ring-color / -ring-widththe role / --mono-ring-widthThe head's focus ring
--mono-accordion-group-gap / -group-radius / -group-border-width / -group-border-color0 / 0 / 0 / --borderThe group. maia, mira, luma and rhea frame it (overflow-hidden rounded-2xl border); a gap turns the list back into separate boxes, and the dividers go with it
--mono-accordion-duration--mono-durationOpen/close animation duration (body collapse + chevron); set 0s for an instant toggle

Performance โ€‹

Vue SFC

An accordion body is always rendered โ€” collapsing it is a visual state, not an unmount โ€” so the consumer's content is never destroyed and never has to be rebuilt on reopen. When a body holds many components, that has two separate costs, and they have different fixes.

Putting a long list of accordions on screen. A collapsed body is skipped for layout and paint entirely (content-visibility: hidden), so closed panels cost almost nothing no matter what is inside them. Measured with ~320-node bodies, initial layout for 40 panels drops from 157 ms to 18 ms, and stops growing with the panel count. This is automatic.

Toggling one panel open or closed. This cost is dominated by the expand animation: grid-template-rows is a layout property, so the browser re-measures the body's whole subtree on every frame. For one open+close cycle with those same bodies, 24.8 ms animated vs 3.6 ms with the animation off. So if your bodies are heavy, turn it off:

css
mono-accordion.heavy { --mono-accordion-duration: 0s; }

That is the single most effective change for a sluggish toggle โ€” roughly 85% of the cost is the animation, not the content.

Two consequences of a collapsed body being skipped, both intentional:

  • Its contents are out of the tab order and the accessibility tree, so Tab no longer lands on invisible form fields inside a closed panel.
  • Its contents are not matched by the browser's in-page find (Ctrl+F).

Browsers without transition-behavior: allow-discrete keep collapsed bodies in layout โ€” the accordion behaves identically, just without the saving.

Types โ€‹

Importimport { AccordionProps } from 'mono-helper'
PropValueDefaultDescription
titlestringโ€”Title text shown in the accordion header. `slot="title"` replaces it, and `slot="header"` replaces the icon, title and subtitle together.
subtitlestringโ€”Secondary text shown under the title. `slot="subtitle"` replaces it.
labelstringโ€”โ€”
descriptionstringโ€”โ€”
modelValuebooleanโ€”Bound open state; true when the accordion is expanded.
disabledbooleanโ€”Disables interaction and dims the accordion.
size'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'โ€”Visual size of the accordion.
color'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'teal' | 'purple' | 'neutral' | 'dark'โ€”Color theme of the accordion.
cssClassAccordionCssClassโ€”Per-element class overrides.