25 Tailwind CSS FAQ Accordion
23 / 25

All 25 in 25 Tailwind CSS FAQ Accordion

  1. 01Tailwind Peer-Checked FAQ Accordion with No JavaScript
  2. 02Tailwind Native Details/Summary FAQ with Custom Styled Icons
  3. 03Tailwind Exclusive Accordion — Only One FAQ Open at a Time
  4. 04Tailwind Multi-Open FAQ Accordion with a Live CSS Counter
  5. 05Tailwind CSS FAQ Accordion Template (Free, Copy-Paste)
  6. 06Tailwind Dark Mode FAQ Accordion with light-dark() and No .dark Class
  7. 07Tailwind Glassmorphism FAQ Accordion with Frosted Backdrop Blur
  8. 08Tailwind Minimalist E-Commerce FAQ Accordion for Product Pages
  9. 09Tailwind Neumorphic Soft-UI FAQ Accordion Cards
  10. 10Tailwind Animate Height Auto FAQ with grid-rows 0fr to 1fr
  11. 11Tailwind FAQ Chevron Rotate 180 Degrees on Toggle
  12. 12Tailwind Animated Plus to Minus Icon Morph FAQ Accordion
  13. 13Tailwind Spring Bounce FAQ Accordion with linear() Easing
  14. 14Tailwind Two-Column FAQ Accordion Grid with Independent Columns
  15. 15Tailwind Horizontal FAQ Accordion with Sideways Expanding Panels
  16. 16Tailwind Categorised FAQ Help Centre with a Sticky Sidebar Menu
  17. 17Tailwind Card-Style FAQ Panels with a Layered Shadow Elevation System
  18. 18Tailwind FAQ Accordion with a Live Search Bar Filter
  19. 19Tailwind Expand All and Collapse All FAQ Accordion Buttons
  20. 20Tailwind FAQ with a Still Have Questions Contact CTA Card
  21. 21Tailwind Accessible ARIA FAQ Accordion with Keyboard Navigation
  22. 22Tailwind FAQ with FAQPage JSON-LD Schema for Rich Snippets and AI Search
  23. 23Bootstrap 5 Accordion Rebuilt in Tailwind (and the JavaScript You Delete)
  24. 24Tailwind FAQ Component in React, Vue, Svelte and Astro (One Port Recipe)
  25. 25Tailwind FAQ with Voice Search Using the Speech Recognition API
Pure TailwindMIT licensed

Bootstrap 5 Accordion Rebuilt in Tailwind (and the JavaScript You Delete)

The migration demo. Bootstrap's accordion needs bootstrap.bundle.js, a data-bs-parent wiring convention and roughly twenty --bs-accordion-* variables to retheme. The same component in Tailwind is a <details name> group plus one class list: identical look and identical exclusive behaviour, with the JavaScript dependency deleted rather than ported.

Live Demo
Plain CSSTailwind v4
Try it

Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49

Cruip · Affiliate link

Browse Cruip templates

The code

<section class="tfa-23 w-full min-h-svh grid place-items-center bg-tfa23-bg font-tfa23-sans text-tfa23-ink px-5 py-12">
  <div class="w-[min(660px,100%)]">
    <header>
      <h1 class="text-[clamp(26px,4.6vw,38px)] font-extrabold tracking-[-0.025em]">The Bootstrap accordion, minus Bootstrap</h1>
      <p class="mt-3 max-w-[56ch] text-[15px] leading-relaxed text-tfa23-mut">Same 48px rows, tinted active header and focus glow. <code class="rounded bg-tfa23-tint px-1.5 py-0.5 font-tfa23-mono text-[0.87em] text-tfa23-acc">data-bs-parent</code> becomes <code class="rounded bg-tfa23-tint px-1.5 py-0.5 font-tfa23-mono text-[0.87em] text-tfa23-acc">name</code>, and the bundle goes away.</p>
    </header>

    <div data-panel class="mt-7 overflow-hidden rounded-[14px] border border-tfa23-edge bg-tfa23-panel">
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0" open>
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">Can I really drop the JavaScript?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">Yes, for the accordion. Exclusivity was the only thing the plugin did that markup could not — and the <em>name</em> attribute does it natively now. Modals and dropdowns are different conversations; disclosure is settled.</p>
        </details>
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0">
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">Which Bootstrap variables map to what?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">The visible ten — background, colour, border colour, radius, button colour, active background, active colour, focus glow and the two icon data-URIs — become a handful of theme tokens plus inline utilities. The paddings you rarely touched need no tokens at all.</p>
        </details>
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0">
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">How do I recolour the chevron now?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">It is an inline SVG with stroke: currentColor, so a text-colour utility does it. No percent-encoded data-URI pair, and no more icons vanishing because a hash was not escaped as %23.</p>
        </details>
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0">
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">Does this survive upgrades?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">Better than either alternative: there is no library version to track. The markup is platform HTML and the styles are utilities compiled from your own config.</p>
        </details>
    </div>

    <p class="mt-5 text-[12.5px] text-tfa23-mut">Zero JS in this demo — the CSS collection's version needed a 12-line collapse shim just to preview.</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tfa23-bg: #141a1f;
  --color-tfa23-bg: oklch(0.19 0.015 240);
  --color-tfa23-panel: #1a2128;
  --color-tfa23-panel: oklch(0.23 0.018 240);
  --color-tfa23-activebg: #1f2d34;
  --color-tfa23-activebg: oklch(0.27 0.03 200);
  --color-tfa23-ink: #eaf0f4;
  --color-tfa23-ink: oklch(0.95 0.008 230);
  --color-tfa23-mut: #b0bcc4;
  --color-tfa23-mut: oklch(0.79 0.015 235);
  --color-tfa23-edge: #333e47;
  --color-tfa23-edge: oklch(0.33 0.022 235);
  --color-tfa23-tint: #1f3a33;
  --color-tfa23-tint: oklch(0.28 0.045 165);
  --color-tfa23-ring: #3f7f68;
  --color-tfa23-ring: oklch(0.55 0.08 165 / 0.45);
  --color-tfa23-acc: #7fd8ab;
  --color-tfa23-acc: oklch(0.84 0.13 165);
  --font-tfa23-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa23-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tfa-23 {
  width: 100%;
  min-height: 100svh;
}

.tfa-23 [data-panel] {
  interpolate-size: allow-keywords;
}

@supports (interpolate-size: allow-keywords) {
  .tfa-23 details::details-content {
    height: 0;
    overflow: clip;
    opacity: 0;
    transition: height .35s cubic-bezier(.4,0,.2,1), opacity .3s, content-visibility .35s allow-discrete;
  }

  .tfa-23 details[open]::details-content {
    height: auto;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tfa-23 details::details-content {
    transition-duration: 1ms;
  }
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tfa23-bg: #141a1f;
  --color-tfa23-bg: oklch(0.19 0.015 240);
  --color-tfa23-panel: #1a2128;
  --color-tfa23-panel: oklch(0.23 0.018 240);
  --color-tfa23-activebg: #1f2d34;
  --color-tfa23-activebg: oklch(0.27 0.03 200);
  --color-tfa23-ink: #eaf0f4;
  --color-tfa23-ink: oklch(0.95 0.008 230);
  --color-tfa23-mut: #b0bcc4;
  --color-tfa23-mut: oklch(0.79 0.015 235);
  --color-tfa23-edge: #333e47;
  --color-tfa23-edge: oklch(0.33 0.022 235);
  --color-tfa23-tint: #1f3a33;
  --color-tfa23-tint: oklch(0.28 0.045 165);
  --color-tfa23-ring: #3f7f68;
  --color-tfa23-ring: oklch(0.55 0.08 165 / 0.45);
  --color-tfa23-acc: #7fd8ab;
  --color-tfa23-acc: oklch(0.84 0.13 165);
  --font-tfa23-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa23-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tfa-23 {
  width: 100%;
  min-height: 100svh;
}

.tfa-23 [data-panel] {
  interpolate-size: allow-keywords;
}

@supports (interpolate-size: allow-keywords) {
  .tfa-23 details::details-content {
    height: 0;
    overflow: clip;
    opacity: 0;
    transition: height .35s cubic-bezier(.4,0,.2,1), opacity .3s, content-visibility .35s allow-discrete;
  }

  .tfa-23 details[open]::details-content {
    height: auto;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tfa-23 details::details-content {
    transition-duration: 1ms;
  }
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The CSS collection's Bootstrap entry had to ship a 40-rule structural imitation of bootstrap.css AND a 12-line collapse shim just to preview standalone. This version has neither: the behaviour is platform HTML and the paint is utilities, so what you read is what runs.
  • name for data-bs-parent is the whole migration in one attribute swap — worth knowing before anyone writes a Tailwind collapse plugin.
  • Bootstrap's chevron is two SVG data-URIs because a background image cannot inherit currentColor. An inline SVG with stroke=&quot;currentColor&quot; deletes both the duplication and the escaping class of bug.
  • focus-visible:ring-3 is Tailwind's one clear win over the variable approach: Bootstrap's focus glow was a full box-shadow value in a variable; here it is a ring utility and a colour token.
  • UI upgrade from the CSS original: the ported theme raises muted copy to oklch(0.79) and the panel to 0.23, where the original's hardcoded hex palette left answers near 4:1; the active header tint is lighter too, so the open row is distinguishable without relying on accent text alone.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Bootstrap 5 Accordion Rebuilt in Tailwind (and the JavaScript You Delete)
Source: https://codefronts.com/snippets/tailwind-css-faq-accordion/bootstrap-5-accordion-rebuilt-in-tailwind/

The migration demo. Bootstrap's accordion needs bootstrap.bundle.js, a data-bs-parent wiring convention and roughly twenty --bs-accordion-* variables to retheme. The same component in Tailwind is a <details name> group plus one class list: identical look and identical exclusive behaviour, with the JavaScript dependency deleted rather than ported.
## HTML
```html
<section class="tfa-23 w-full min-h-svh grid place-items-center bg-tfa23-bg font-tfa23-sans text-tfa23-ink px-5 py-12">
  <div class="w-[min(660px,100%)]">
    <header>
      <h1 class="text-[clamp(26px,4.6vw,38px)] font-extrabold tracking-[-0.025em]">The Bootstrap accordion, minus Bootstrap</h1>
      <p class="mt-3 max-w-[56ch] text-[15px] leading-relaxed text-tfa23-mut">Same 48px rows, tinted active header and focus glow. <code class="rounded bg-tfa23-tint px-1.5 py-0.5 font-tfa23-mono text-[0.87em] text-tfa23-acc">data-bs-parent</code> becomes <code class="rounded bg-tfa23-tint px-1.5 py-0.5 font-tfa23-mono text-[0.87em] text-tfa23-acc">name</code>, and the bundle goes away.</p>
    </header>

    <div data-panel class="mt-7 overflow-hidden rounded-[14px] border border-tfa23-edge bg-tfa23-panel">
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0" open>
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">Can I really drop the JavaScript?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">Yes, for the accordion. Exclusivity was the only thing the plugin did that markup could not — and the <em>name</em> attribute does it natively now. Modals and dropdowns are different conversations; disclosure is settled.</p>
        </details>
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0">
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">Which Bootstrap variables map to what?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">The visible ten — background, colour, border colour, radius, button colour, active background, active colour, focus glow and the two icon data-URIs — become a handful of theme tokens plus inline utilities. The paddings you rarely touched need no tokens at all.</p>
        </details>
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0">
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">How do I recolour the chevron now?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">It is an inline SVG with stroke: currentColor, so a text-colour utility does it. No percent-encoded data-URI pair, and no more icons vanishing because a hash was not escaped as %23.</p>
        </details>
        <details name="tfa23-acc" class="group border-b border-tfa23-edge last:border-b-0">
          <summary class="flex min-h-12 cursor-pointer list-none items-center gap-4 bg-transparent px-[18px] py-4 text-[15px] font-semibold text-tfa23-ink transition-colors duration-250 hover:text-tfa23-acc focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-tfa23-ring group-open:bg-tfa23-activebg group-open:text-tfa23-acc [&::-webkit-details-marker]:hidden">Does this survive upgrades?
            <span aria-hidden="true" class="ml-auto shrink-0 text-tfa23-mut transition-[rotate,color] duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:-rotate-180 group-open:text-tfa23-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="18" height="18"><path d="M3 6l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
          </summary>
          <p class="px-[18px] pb-[17px] text-[13.5px] leading-[1.7] text-tfa23-mut">Better than either alternative: there is no library version to track. The markup is platform HTML and the styles are utilities compiled from your own config.</p>
        </details>
    </div>

    <p class="mt-5 text-[12.5px] text-tfa23-mut">Zero JS in this demo — the CSS collection's version needed a 12-line collapse shim just to preview.</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tfa23-bg: #141a1f;
  --color-tfa23-bg: oklch(0.19 0.015 240);
  --color-tfa23-panel: #1a2128;
  --color-tfa23-panel: oklch(0.23 0.018 240);
  --color-tfa23-activebg: #1f2d34;
  --color-tfa23-activebg: oklch(0.27 0.03 200);
  --color-tfa23-ink: #eaf0f4;
  --color-tfa23-ink: oklch(0.95 0.008 230);
  --color-tfa23-mut: #b0bcc4;
  --color-tfa23-mut: oklch(0.79 0.015 235);
  --color-tfa23-edge: #333e47;
  --color-tfa23-edge: oklch(0.33 0.022 235);
  --color-tfa23-tint: #1f3a33;
  --color-tfa23-tint: oklch(0.28 0.045 165);
  --color-tfa23-ring: #3f7f68;
  --color-tfa23-ring: oklch(0.55 0.08 165 / 0.45);
  --color-tfa23-acc: #7fd8ab;
  --color-tfa23-acc: oklch(0.84 0.13 165);
  --font-tfa23-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa23-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tfa-23 {
  width: 100%;
  min-height: 100svh;
}

.tfa-23 [data-panel] {
  interpolate-size: allow-keywords;
}

@supports (interpolate-size: allow-keywords) {
  .tfa-23 details::details-content {
    height: 0;
    overflow: clip;
    opacity: 0;
    transition: height .35s cubic-bezier(.4,0,.2,1), opacity .3s, content-visibility .35s allow-discrete;
  }

  .tfa-23 details[open]::details-content {
    height: auto;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tfa-23 details::details-content {
    transition-duration: 1ms;
  }
}
```

How this works

What Bootstrap needs, and why. Its accordion is a JS widget: the button toggles a .collapsed class, the plugin measures and animates the panel, and data-bs-parent tells the plugin which siblings to close. Theming happens through about twenty CSS variables, including two SVG data-URIs for the chevron, because a background image cannot inherit currentColor.

What replaces it. The name attribute gives exclusivity natively (demo 03), <details> gives state and keyboard support, group-open: gives the active styling, and the chevron is an inline SVG with stroke="currentColor" — so recolouring it is a text-colour utility instead of two percent-encoded data-URIs. That last point is the biggest quality-of-life difference in this port: no more %23 escaping bugs that silently blank the icon.

Kept deliberately Bootstrap-looking: the 48px rows, the tinted active header, the glow-style focus ring and the flush divider treatment, so this can drop into a Bootstrap-era design without a redesign conversation. What changed is the dependency list — from a 60kB bundle to zero.

When to stay on Bootstrap: when the rest of the page is Bootstrap. This demo is for the migration case, not an argument to rip out a working design system.

Make it yours

  • Multi-open: delete the name attribute and several panels can stay open — Bootstrap needs a config change for that, you need a deletion.
  • Theme: the tokens here cover what twenty --bs-accordion-* variables did, because utilities handle the paddings and radii inline.
  • Flush variant: drop the outer border and radius for Bootstrap's flush look.
  • Icon: swap the SVG path; it inherits colour automatically, unlike Bootstrap's data-URI pair.

Gotchas — read before shipping

  • If you keep Bootstrap's CSS on the page, its .accordion-* rules will fight these utilities. Scope the migration per component.
  • Bootstrap animates height in JS; <details> needs the ::details-content block for the same effect, and degrades to instant open where that is unsupported.
  • name-based exclusivity is Chrome 120 / Safari 17.2 / Firefox 130. Older browsers fall back to multi-open, where Bootstrap's plugin would still close siblings.
  • Do not port data-bs-toggle/data-bs-target attributes for decoration. Dead wiring outlives the library and confuses the next developer.

Browser support

ChromeSafariFirefoxEdge
129+not yetnot yet129+

Floor set by oklch(), interpolate-size as this demo is written. The core technique itself goes back further — Chrome 120+.

The floor is details[name] exclusivity; without it the FAQ degrades to multi-open rather than breaking. The animated panel adds Chrome 131 / Safari 18.4 with an instant-open fallback.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…