Skip to content

Accordion ​

An accordion is a set of sections that share a frame: one border round the lot, a rule between each. That framing is the difference from a DisclosureGroup, which coordinates the same expanding behaviour without drawing anything — reach for the group when the sections are already inside cards or a layout of their own.

ts
import {
  Accordion,
  AccordionBody,
  AccordionHeading,
  AccordionIndicator,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "ropav";

AccordionItem is one section. Inside it, AccordionHeading holds AccordionTrigger — the pressable row — and AccordionPanel holds AccordionBody, the padded box that appears.

Every component ships its own stylesheet, and the theme decides the colours.

accordion-basic.vue
vue
<script setup lang="ts">
import {
  Accordion,
  AccordionBody,
  AccordionHeading,
  AccordionIndicator,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "ropav";

const questions = [
  {
    answer: "Every component ships its own stylesheet, and the theme decides the colours.",
    id: "styling",
    title: "How is it styled?",
  },
  {
    answer: "One tab stop per control, and the keyboard interactions the ARIA pattern asks for.",
    id: "keyboard",
    title: "What about the keyboard?",
  },
  {
    answer: "Server rendering works, and the client hydrates what the server drew.",
    id: "ssr",
    title: "Does it render on the server?",
  },
] as const;
</script>

<template>
  <Accordion class="panel" :default-expanded-keys="['styling']">
    <AccordionItem v-for="question in questions" :id="question.id" :key="question.id">
      <AccordionHeading>
        <AccordionTrigger>
          {{ question.title }}
          <AccordionIndicator />
        </AccordionTrigger>
      </AccordionHeading>
      <AccordionPanel>
        <AccordionBody>{{ question.answer }}</AccordionBody>
      </AccordionPanel>
    </AccordionItem>
  </Accordion>
</template>

<style scoped>
.panel {
  width: 100%;
  max-width: var(--rp-container-md);
}
</style>

One at a time, or several ​

By default opening a section closes the last. allows-multiple-expanded lets them stand open together, which is what you want when the sections are reference material a reader compares rather than steps they work through.

Each item needs an id to be keyed by — one is generated if you leave it out, but then expanded-keys has nothing stable to name. default-expanded-keys starts the accordion somewhere and v-model:expanded-keys hands the state to the caller.

hide-separator drops the rules between items, and is-disabled on the accordion disables every item in it while an item can still disable itself.

Accessibility ​

  • Every trigger is inside a real heading, so the sections appear in a document outline and a reader navigating by heading finds them.
  • The trigger carries aria-expanded and aria-controls, and the panel is what those point at.
  • The panel is a role="group" by default. Where a section is a region of the page in its own right, role="region" on AccordionPanel says so.
  • With one section open at a time, opening one closes another under the reader. That is a reason to prefer allows-multiple-expanded wherever the sections are things to read rather than a sequence to work through.

API ​

Accordion

PropTypeDefaultDescription
allowsMultipleExpandedboolean—Whether more than one item can be expanded at a time.
defaultExpandedKeysIterable<DisclosureKey>—Expanded item keys in uncontrolled mode.
expandedKeysIterable<DisclosureKey>—Expanded item keys in controlled mode. Pairs with v-model:expanded-keys.
hideSeparatorbooleanfalseRemoves the separator drawn between items.
isDisabledboolean—Disables every item in the accordion.
variant"default" | "surface"—Visual variant.

Slots: default.

EventPayload
expandedChangeSet<DisclosureKey>
update:expandedKeysSet<DisclosureKey>

AccordionBody

Slots: default.

AccordionHeading

Slots: default.

AccordionIndicator

Slots: default.

AccordionItem

PropTypeDefaultDescription
idstring—Identity of the item within the group, and the value carried in expandedKeys. Generated when omitted.
isDisabledboolean—Disables this item only.

Slots: default.

AccordionPanel

PropTypeDefaultDescription
rolestring"group"ARIA role of the panel. Matches React Aria's default.

Slots: default.

AccordionTrigger

Slots: default.