23 / 25
All 25 in 25 Tailwind CSS FAQ Accordion
- 01Tailwind Peer-Checked FAQ Accordion with No JavaScript
- 02Tailwind Native Details/Summary FAQ with Custom Styled Icons
- 03Tailwind Exclusive Accordion — Only One FAQ Open at a Time
- 04Tailwind Multi-Open FAQ Accordion with a Live CSS Counter
- 05Tailwind CSS FAQ Accordion Template (Free, Copy-Paste)
- 06Tailwind Dark Mode FAQ Accordion with light-dark() and No .dark Class
- 07Tailwind Glassmorphism FAQ Accordion with Frosted Backdrop Blur
- 08Tailwind Minimalist E-Commerce FAQ Accordion for Product Pages
- 09Tailwind Neumorphic Soft-UI FAQ Accordion Cards
- 10Tailwind Animate Height Auto FAQ with grid-rows 0fr to 1fr
- 11Tailwind FAQ Chevron Rotate 180 Degrees on Toggle
- 12Tailwind Animated Plus to Minus Icon Morph FAQ Accordion
- 13Tailwind Spring Bounce FAQ Accordion with linear() Easing
- 14Tailwind Two-Column FAQ Accordion Grid with Independent Columns
- 15Tailwind Horizontal FAQ Accordion with Sideways Expanding Panels
- 16Tailwind Categorised FAQ Help Centre with a Sticky Sidebar Menu
- 17Tailwind Card-Style FAQ Panels with a Layered Shadow Elevation System
- 18Tailwind FAQ Accordion with a Live Search Bar Filter
- 19Tailwind Expand All and Collapse All FAQ Accordion Buttons
- 20Tailwind FAQ with a Still Have Questions Contact CTA Card
- 21Tailwind Accessible ARIA FAQ Accordion with Keyboard Navigation
- 22Tailwind FAQ with FAQPage JSON-LD Schema for Rich Snippets and AI Search
- 23Bootstrap 5 Accordion Rebuilt in Tailwind (and the JavaScript You Delete)
- 24Tailwind FAQ Component in React, Vue, Svelte and Astro (One Port Recipe)
- 25Tailwind FAQ with Voice Search Using the Speech Recognition API
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.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
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><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;
}
}@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.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@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;
}
}@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;
}
}<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">
<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.cssAND 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. namefordata-bs-parentis 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 withstroke="currentColor"deletes both the duplication and the escaping class of bug. focus-visible:ring-3is 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 to0.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.
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;
}
}
```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
nameattribute 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-contentblock 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-targetattributes for decoration. Dead wiring outlives the library and confuses the next developer.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 129+ | not yet | not yet | 129+ |
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.