FAQ Accordion
An accessible accordion where one answer opens at a time.
6 frameworksIntermediate
An FAQ accordion with a search box that filters the questions as you type.
<!--
Progressive enhancement: with JS off this is a plain <details> list and the
search field simply does nothing visible - every answer is still readable.
The filtering itself is ~10 lines; see the React tabs for the stateful version.
The result count lives in an aria-live="polite" region so a screen-reader user
hears "2 questions" as they type, instead of the list silently reflowing.
-->
<div class="acc-search">
<form class="acc-search__form" role="search" onsubmit="return false">
<label class="acc-search__label" for="faq-search">Search questions</label>
<input class="acc-search__input" id="faq-search" type="search" placeholder="Search questions…" autocomplete="off" />
</form>
<p class="acc-search__count" role="status" aria-live="polite">3 questions</p>
<div class="acc-search__list">
<details class="acc-search__item" name="acc-search" open>
<summary class="acc-search__question">
Can I export my data?
<svg class="acc-search__icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="m5 7.5 5 5 5-5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</summary>
<div class="acc-search__answer">
<p>Any time, from Settings → Export. You get a ZIP of CSV and JSON - no lock-in and no support ticket.</p>
</div>
</details>
<details class="acc-search__item" name="acc-search">
<summary class="acc-search__question">
Do you have a mobile app?
<svg class="acc-search__icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="m5 7.5 5 5 5-5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</summary>
<div class="acc-search__answer">
<p>Native apps for iOS and Android, plus an installable web app that works offline.</p>
</div>
</details>
<details class="acc-search__item" name="acc-search">
<summary class="acc-search__question">
Which integrations are supported?
<svg class="acc-search__icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="m5 7.5 5 5 5-5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</summary>
<div class="acc-search__answer">
<p>Slack, GitHub, Jira, Zapier and around forty more, all managed from the integrations directory.</p>
</div>
</details>
</div>
</div>| Prop | Type | Default | Description |
|---|---|---|---|
itemsrequired | FaqItem[] | - | The array of items to render. |
defaultOpenId | string | - | Id of the item expanded on mount. |
className | string | - | Additional classes merged onto the root element. |
Filtering matches answers as well as questions, so a search for "refund" finds a question whose title never uses the word - the substring match is the piece to replace first if you outgrow it. The result count sits in an `aria-live="polite"` region so a screen-reader user hears the list change instead of it silently reflowing beneath them, and the label is `sr-only` rather than absent, because a placeholder is not a label. The HTML tabs degrade to a plain `<details>` list: with JavaScript off the field does nothing and every answer stays readable.