Accordion
Accordion
The Accordion component renders a list of expandable question and answer rows: a semibold question on the left, a plus or minus icon on the right, and the answer revealed underneath when the row is open. Rows sit directly on the page background and are separated by hairlines. It powers the FAQ on the Programmable Tokens page, every FAQ Section, and the FAQ on the Governance page, and works for any short list of disclosures.
Features
- Accessible toggles - every row is a real
<button>inside a heading (an<h3>by default, seeheadingLevel) witharia-expandedandaria-controls; each answer is a labeledrole="region", and a closed answer is hidden from the tab order and from assistive technology. When a row closes while focus is in its answer, focus returns to the row's button - Smooth open and close - answers expand and collapse with a height transition done in CSS (a grid row going from
0frto1fr), with no JavaScript measuring - Answers in the HTML - every answer is rendered, open or closed, so the full text is available to search engines and without JavaScript
- Single-open by default - opening a row closes the others; pass
allowMultipleto keep several open at once - Optional starting state -
defaultOpenIndexopens one row on first render - Rich answers - string arrays support
-bullet lines,[text](url)links, and**bold**, or pass any React node - Dark mode support - text, borders, and icons use theme-aware tokens, and there is no card surface to invert
- Reduced motion - the open and close transition and the hover color change are turned off for users who prefer reduced motion, so rows open instantly
Basic Usage
import Accordion from '@site/src/components/Layout/Accordion';
const items = [
{
question: "What are programmable tokens?",
answer: [
"Native tokens whose transfers have to pass rules set by their issuer, for example:",
"- An allowlist of verified holders",
"- A denylist of sanctioned addresses",
],
},
{
question: "Where can I learn more?",
answer: ["Start with the [programmable token](/glossary/programmable-token) glossary entry."],
},
];
<Accordion items={items} defaultOpenIndex={0} />
Keep the questions and answers in a data module or JSON file wrapped with @docusaurus/Translate, as src/data/programmable-tokens.js does, rather than writing them inline in JSX.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
items | array | Yes | List of { question, answer } objects, in display order. |
defaultOpenIndex | number | No | Index of the row that starts open, or an array of indexes. Defaults to null (all closed). Ignored when openKeys is set. |
allowMultiple | boolean | No | When true, opening a row does not close the others. Defaults to false. Ignored when openKeys is set. |
openKeys | array | No | Open rows for controlled use, by id or else by index. With it, the page owns the state: rows only open or close when the page updates openKeys in onToggle. [] closes all. |
onToggle | function | No | Called with (key, nextIsOpen) when the reader opens or closes a row. key is the row's id, or its index when it has none. |
headingLevel | number | No | Heading level of each question, defaults to 3. 0 renders the question without heading semantics. The look stays the same. |
className | string | No | Extra class name for the wrapper element. |
Item properties
Each object in items has these properties:
| Property | Type | Required | Description |
|---|---|---|---|
id | string | No | Set as the row's HTML id, so a link or a script can scroll to it, and used as the row's key in openKeys. Must be unique on the page. |
question | string | Yes | The row heading, rendered as the button label. |
answer | string[] or React.ReactNode | Yes | Either an array of strings (one paragraph each; lines starting with - become bullet points, and [text](url) and **bold** are supported) or a React node. |
Live Demo
Programmable tokens are native tokens that can only change owner when a script approves the transfer, so the issuer can enforce rules such as allowlists, transfer limits, or freezes.
Each string in the array becomes a paragraph. Lines starting with a dash become bullet points:
- Bold text with double asterisks
- Links such as the programmable token glossary entry
- Plain text
Yes. Anything that renders, such as this paragraph element, can be passed as the answer.
Controlled use
Pass openKeys and onToggle when the page decides which rows are open, for example to keep one question open across several lists or to open a question from a link elsewhere on the page. Give the rows an id, because indexes repeat between lists.
const [openId, setOpenId] = useState(null);
const openKeys = openId !== null ? [openId] : [];
const onToggle = (id, nextIsOpen) => setOpenId(nextIsOpen ? id : null);
<Accordion items={delegationQuestions} openKeys={openKeys} onToggle={onToggle} />
<Accordion items={votingQuestions} openKeys={openKeys} onToggle={onToggle} />
<button onClick={() => setOpenId('what-is-a-drep')}>What is a DRep?</button>
Yes, delegation keeps your ada in your wallet.
Stake delegation earns rewards every epoch.
A delegated representative votes on governance actions.
Only one question is open across both lists, and the button opens a question from outside.
Heading level
headingLevel only changes the element around each question, the look stays the same. The three lists below use level 3 (default), 5, and 0.
Yes. Only the element in the page outline changes.
Yes. Only the element in the page outline changes.
Yes. Only the element in the page outline changes.