25 Tailwind CSS FAQ Accordion
03 / 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
Tailwind v4MIT licensed

Tailwind Exclusive Accordion — Only One FAQ Open at a Time

Only-one-open used to mean JavaScript closing siblings; now it is one HTML attribute. Give every <details> the same name and the browser enforces exclusivity like radio buttons. The port's contribution is the attention choreography: has-[details[open]]: on the list dims the closed questions, which is :has() written where you can see it.

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-03 w-full min-h-svh grid place-items-center bg-tfa03-bg font-tfa03-sans text-tfa03-ink px-5 py-12 bg-[linear-gradient(180deg,var(--color-tfa03-wash),var(--color-tfa03-bg)_42%)]">
  <div class="w-[min(680px,100%)]">
    <header>
      <span class="inline-block rounded-full bg-tfa03-tint px-3 py-1.5 font-tfa03-mono text-[11.5px] font-semibold text-tfa03-acc">name="faq" · zero-JS exclusivity</span>
      <h1 class="mt-4 text-[clamp(28px,5vw,40px)] font-extrabold leading-[1.08] tracking-[-0.025em]">Pricing questions, answered</h1>
      <p class="mt-3 max-w-[54ch] text-[15px] leading-relaxed text-tfa03-mut">Open any question — the browser closes the previous one. One attribute, no JavaScript.</p>
    </header>

    <div data-panel class="mt-8 flex flex-col gap-2.5 has-[details[open]]:[&>details:not([open])]:opacity-70">
      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]" open>
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">01</span>Is there a free plan?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Yes — free forever for up to 3 projects and 2 seats, with no card required. It is a real plan, not a trial: teams ship production work on it every day.</p>
      </details>

      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]">
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">02</span>What counts as a “seat”?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Anyone who can edit. Viewers and commenters are unlimited and free on every plan — you only pay for people who change things.</p>
      </details>

      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]">
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">03</span>Do prices include tax?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Prices are shown pre-tax; VAT/GST is added at checkout based on your billing country. US customers see applicable state sales tax, and every invoice is compliant for expensing.</p>
      </details>

      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]">
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:-outline-offset-2 focus-visible:outline-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">04</span>Can I pay by invoice?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Annual plans over $2,000 can be paid by bank transfer with NET-30 terms. Contact sales and we will set up procurement, security review and a countersigned DPA if you need one.</p>
      </details>
    </div>

    <p class="mt-6 text-[12.5px] text-tfa03-mut">Closed questions dim via <code class="rounded bg-tfa03-tint px-1.5 py-0.5 font-tfa03-mono text-[0.88em] text-tfa03-acc">has-[details[open]]:</code> — attention follows the open panel.</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tfa03-bg: #fafafd;
  --color-tfa03-bg: oklch(0.98 0.005 270);
  --color-tfa03-wash: #eeecfb;
  --color-tfa03-wash: oklch(0.94 0.025 275);
  --color-tfa03-ink: #1f1d2b;
  --color-tfa03-ink: oklch(0.22 0.03 275);
  --color-tfa03-mut: #5c5a6e;
  --color-tfa03-mut: oklch(0.48 0.025 275);
  --color-tfa03-edge: #e3e1ee;
  --color-tfa03-edge: oklch(0.91 0.012 275);
  --color-tfa03-accedge: #9b8ce0;
  --color-tfa03-accedge: oklch(0.72 0.1 285);
  --color-tfa03-tint: #eeebfd;
  --color-tfa03-tint: oklch(0.94 0.03 285);
  --color-tfa03-acc: #5b3fd0;
  --color-tfa03-acc: oklch(0.47 0.2 285);
  --font-tfa03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa03-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tfa-03 {
  width: 100%;
  min-height: 100svh;
}
/* Same irreducible four lines as demo 02 — the animated panel, once, for the whole list. */

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

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

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

@media (prefers-reduced-motion: reduce) {
  .tfa-03 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-tfa03-bg: #fafafd;
  --color-tfa03-bg: oklch(0.98 0.005 270);
  --color-tfa03-wash: #eeecfb;
  --color-tfa03-wash: oklch(0.94 0.025 275);
  --color-tfa03-ink: #1f1d2b;
  --color-tfa03-ink: oklch(0.22 0.03 275);
  --color-tfa03-mut: #5c5a6e;
  --color-tfa03-mut: oklch(0.48 0.025 275);
  --color-tfa03-edge: #e3e1ee;
  --color-tfa03-edge: oklch(0.91 0.012 275);
  --color-tfa03-accedge: #9b8ce0;
  --color-tfa03-accedge: oklch(0.72 0.1 285);
  --color-tfa03-tint: #eeebfd;
  --color-tfa03-tint: oklch(0.94 0.03 285);
  --color-tfa03-acc: #5b3fd0;
  --color-tfa03-acc: oklch(0.47 0.2 285);
  --font-tfa03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa03-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tfa-03 {
  width: 100%;
  min-height: 100svh;
}
/* Same irreducible four lines as demo 02 — the animated panel, once, for the whole list. */

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

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

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

@media (prefers-reduced-motion: reduce) {
  .tfa-03 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;600&display=swap">

What changed in the port

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

  • .list:has([open]) .item:not([open]) summary becomes has-[details[open]]:[&>details:not([open])]:opacity-70 on the list. Same three conditions, one class, declared on the element that owns the rule — and *: (direct children) is what makes it expressible without a custom selector.
  • The accent rail moved from .item::before to a real <span aria-hidden>. Pseudo-elements are stylable in Tailwind but a per-state before:scale-y-* chain is longer and invisible in devtools; a 3px span costs one element and reads better.
  • open: and group-open: cover every state style here, so the whole [open] attribute-selector family disappeared from the stylesheet. Only the ::details-content block remains.
  • UI upgrade from the CSS original: muted copy went oklch(0.52) → 0.48 and the accent 0.55 → 0.47, clearing 4.5:1 on white where the original's dimmed-at-72% rows did not. Dimming itself is now opacity-70 on the whole item rather than on the summary, so the chevron dims with its row.
  • UI upgrade: the original relied on :hover{opacity:1 !important} to undo its own dimming — a smell. Here the hover state changes ink colour instead, so no !important and no fighting the cascade.
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: Tailwind Exclusive Accordion — Only One FAQ Open at a Time
Source: https://codefronts.com/snippets/tailwind-css-faq-accordion/tailwind-exclusive-accordion-only-one-faq-open/

Only-one-open used to mean JavaScript closing siblings; now it is one HTML attribute. Give every <details> the same name and the browser enforces exclusivity like radio buttons. The port's contribution is the attention choreography: has-[details[open]]: on the list dims the closed questions, which is :has() written where you can see it.
## HTML
```html
<section class="tfa-03 w-full min-h-svh grid place-items-center bg-tfa03-bg font-tfa03-sans text-tfa03-ink px-5 py-12 bg-[linear-gradient(180deg,var(--color-tfa03-wash),var(--color-tfa03-bg)_42%)]">
  <div class="w-[min(680px,100%)]">
    <header>
      <span class="inline-block rounded-full bg-tfa03-tint px-3 py-1.5 font-tfa03-mono text-[11.5px] font-semibold text-tfa03-acc">name="faq" · zero-JS exclusivity</span>
      <h1 class="mt-4 text-[clamp(28px,5vw,40px)] font-extrabold leading-[1.08] tracking-[-0.025em]">Pricing questions, answered</h1>
      <p class="mt-3 max-w-[54ch] text-[15px] leading-relaxed text-tfa03-mut">Open any question — the browser closes the previous one. One attribute, no JavaScript.</p>
    </header>

    <div data-panel class="mt-8 flex flex-col gap-2.5 has-[details[open]]:[&>details:not([open])]:opacity-70">
      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]" open>
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">01</span>Is there a free plan?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Yes — free forever for up to 3 projects and 2 seats, with no card required. It is a real plan, not a trial: teams ship production work on it every day.</p>
      </details>

      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]">
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">02</span>What counts as a “seat”?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Anyone who can edit. Viewers and commenters are unlimited and free on every plan — you only pay for people who change things.</p>
      </details>

      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]">
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">03</span>Do prices include tax?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Prices are shown pre-tax; VAT/GST is added at checkout based on your billing country. US customers see applicable state sales tax, and every invoice is compliant for expensing.</p>
      </details>

      <details name="tfa03-faq" class="group relative overflow-hidden rounded-2xl border border-tfa03-edge bg-white transition-[border-color,box-shadow,opacity] duration-300 open:border-tfa03-accedge open:shadow-[0_14px_34px_-18px_oklch(0.45_0.14_275/0.35)]">
        <span aria-hidden="true" class="absolute inset-y-0 left-0 w-[3px] origin-top scale-y-0 bg-tfa03-acc transition-transform duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] group-open:scale-y-100 motion-reduce:transition-none"></span>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3.5 px-5 py-4 text-[15.5px] font-semibold transition-colors duration-200 hover:text-tfa03-acc focus-visible:rounded-2xl focus-visible:-outline-offset-2 focus-visible:outline-2 focus-visible:outline-tfa03-acc [&::-webkit-details-marker]:hidden">
          <span class="font-tfa03-mono text-[11px] font-bold text-tfa03-acc">04</span>Can I pay by invoice?
          <i aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa03-mut transition-[rotate,translate,border-color] duration-350 group-hover:border-tfa03-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa03-acc motion-reduce:transition-none"></i>
        </summary>
        <p class="max-w-[58ch] pb-4 pl-12 pr-5 text-[14.5px] leading-[1.7] text-tfa03-mut">Annual plans over $2,000 can be paid by bank transfer with NET-30 terms. Contact sales and we will set up procurement, security review and a countersigned DPA if you need one.</p>
      </details>
    </div>

    <p class="mt-6 text-[12.5px] text-tfa03-mut">Closed questions dim via <code class="rounded bg-tfa03-tint px-1.5 py-0.5 font-tfa03-mono text-[0.88em] text-tfa03-acc">has-[details[open]]:</code> — attention follows the open panel.</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tfa03-bg: #fafafd;
  --color-tfa03-bg: oklch(0.98 0.005 270);
  --color-tfa03-wash: #eeecfb;
  --color-tfa03-wash: oklch(0.94 0.025 275);
  --color-tfa03-ink: #1f1d2b;
  --color-tfa03-ink: oklch(0.22 0.03 275);
  --color-tfa03-mut: #5c5a6e;
  --color-tfa03-mut: oklch(0.48 0.025 275);
  --color-tfa03-edge: #e3e1ee;
  --color-tfa03-edge: oklch(0.91 0.012 275);
  --color-tfa03-accedge: #9b8ce0;
  --color-tfa03-accedge: oklch(0.72 0.1 285);
  --color-tfa03-tint: #eeebfd;
  --color-tfa03-tint: oklch(0.94 0.03 285);
  --color-tfa03-acc: #5b3fd0;
  --color-tfa03-acc: oklch(0.47 0.2 285);
  --font-tfa03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa03-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tfa-03 {
  width: 100%;
  min-height: 100svh;
}
/* Same irreducible four lines as demo 02 — the animated panel, once, for the whole list. */

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

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

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

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

How this works

Exclusivity is an attribute. Same-name details behave like a radio group: at most one holds open, and the browser removes it from the previously-open element. No listeners, no state array — and because the platform does the closing, proper toggle events still fire if you want analytics.

<details name="tfa03-faq" class="group" open>…</details>
<details name="tfa03-faq" class="group">…</details>

The dimming is the interesting port. The CSS version wrote .list:has([open]) .item:not([open]) summary{opacity:.72} — three selectors deep, far from the markup. v4 states it on the list as a variant stack:

class="has-[details[open]]:[&>details:not([open])]:opacity-70"

Read it right to left: any direct child that is not open, when the list has an open details, dims. *: (the direct-children variant) is what makes this expressible at all — without it you would be writing a plain CSS rule.

The accent rail became a real element. Tailwind can style before:, but animating a pseudo-element's scale per-state reads badly in a class list; a 3px <span> with origin-top scale-y-0 group-open:scale-y-100 is shorter, inspectable in devtools, and identical on screen.

UX note before shipping: exclusive accordions read tidy but punish comparison. Use only-one-open when answers are long and independent (pricing objections, legal); use demo 04's multi-open when users cross-reference.

Make it yours

  • Independent groups on one page: different name values, e.g. name="billing" and name="security".
  • Start with one open: put open on exactly one item — two are auto-resolved to the last.
  • Dimming strength: opacity-70 is gentle; opacity-60 is directive, opacity-80 nearly invisible.
  • Legacy target? Swap to demo 01's markup with type="radio" name="faq" for identical UX in pre-2023 browsers.

Gotchas — read before shipping

  • The name must be an identical string — faq and Faq are two groups.
  • Never set open on two same-name details in source: browsers keep only the last, which can surprise a server-rendered diff.
  • has-[details[open]]: needs the bracket form; there is no built-in shorthand for "has an open details descendant".
  • Deep-linking into a collapsed panel needs the open attribute added server-side, or the fragment scrolls to a closed item.

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+.

details[name] exclusivity shipped across engines by late 2024. In older browsers the attribute is ignored and the FAQ degrades to multi-open — nothing breaks. :has() drives the dimming (Firefox 121); without it closed items simply stay at full opacity.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…