NbAccordion is a stack of headings that reveal their content when clicked. It is the plain disclosure for page body: NbShellPanel looks similar but is an inspector section, carrying a size control and expecting a panel rail around it.
<NbAccordion>
<NbAccordionItem title="What a blueprint card is">
A node in a graph.
</NbAccordionItem>
<NbAccordionItem title="What a port is"> One element. </NbAccordionItem>
</NbAccordion>When to use it
Carbon's guidance holds here, and it is mostly about restraint.
- Do use it to shorten a page whose content is not all essential, and to group related detail a reader can choose to open.
- Don't use it when the reader is likely to want everything. An accordion then charges them a click per section for no benefit; a scrolling page with ordinary headings is better.
- Don't use it for nested hierarchy. Reach for
NbTreeinstead.
Content inside a closed panel stays in the DOM, so find-in-page still reaches it. That is deliberate: an accordion is progressive disclosure, not lazy loading. If a section is genuinely expensive, defer it yourself from the toggle event.
One open, or several
By default opening a section closes the last one, which keeps the page short. Set multiple when sections are meant to be compared.
Icons and meta
icon puts a mark before the title; meta puts a count or a status after it, before the chevron.
Flush, for panels and sidebars
flush drops the side inset so titles line up with the content above them, and stops two nearly-touching rule lines appearing where a bordered accordion meets a bordered panel. Hover and focus still reach into the gutter, so a row is still a row.
Sizes
Chevron placement
The chevron sits at the end by default, so every title starts on the same line as the type around it. align="start" puts it in front, which makes the accordion read as a tree; use it only when the content really is hierarchical.
Controlled
Bind v-model to drive which sections are open from outside, for example to open one from a URL. The value is always an array, including when multiple is false, so the binding does not change shape if you change your mind later.
<script setup>
const open = ref(['shipping'])
</script>
<template>
<NbAccordion v-model="open" multiple>
<NbAccordionItem id="shipping" title="Shipping" />
<NbAccordionItem id="returns" title="Returns" />
</NbAccordion>
</template>Pass an explicit id on every item when you do this. Without one an id is generated, and a generated id is not a thing you can meaningfully persist.
Keyboard
| Key | Does |
|---|---|
| Tab | Moves to the next header, skipping the panels' contents |
| Enter / Space | Opens or closes the focused section |
| ↑ / ↓ | Moves between headers, wrapping |
| Home / End | Jumps to the first or last header |
Each header is a <button> inside a role="heading" element. The heading is what lets a screen reader list the sections; the button is what operates them.
Set headingLevel to fit the accordion into the page's outline. It defaults to 3.
<NbAccordion :heading-level="2">
<NbAccordionItem title="Under an h1" />
</NbAccordion>The heading is a role="heading" div rather than an <h3> on purpose. A real <h3> inherits any some-container h3 { margin } the host page has, and an element selector inside a class outranks a component's own scoped rule, so the component cannot defend against it. That is not theoretical: it was adding 20px above every title in these very docs.