25 Tailwind CSS FAQ Accordion
11 / 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 FAQ Chevron Rotate 180 Degrees on Toggle

The definitive treatment of the web's most-copied micro-interaction, ported: three chevron builds in one FAQ — inline SVG, a two-border CSS chevron, and a Unicode glyph — each rotating on group-open: with matched timing, plus the UX rules for which way a closed chevron should point and why the rotation duration must equal the panel's.

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-11 w-full min-h-svh grid place-items-center bg-tfa11-bg font-tfa11-sans text-tfa11-ink px-5 py-12">
  <div class="w-[min(680px,100%)]">
    <header>
      <h1 class="text-[clamp(28px,5vw,40px)] font-extrabold leading-[1.08] tracking-[-0.025em]">The 180° chevron, three ways</h1>
      <p class="mt-3 max-w-[56ch] text-[15px] leading-relaxed text-tfa11-mut">Same rotation, three icon builds — SVG, pure CSS borders, Unicode. Matched <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">duration-400</code> keeps icon and panel in sync.</p>
    </header>

    <div data-panel class="mt-8 flex flex-col gap-2.5">
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]" open>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">A · SVG</span>Which icon build should production use?
          <span aria-hidden="true" class="ml-auto grid shrink-0 place-items-center text-tfa11-mut transition-[rotate,color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:text-tfa11-acc group-open:rotate-180 group-open:text-tfa11-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="16" height="16"><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="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">This one. <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">stroke: currentColor</code> inherits your text colour automatically, the rounded caps stay crisp at every DPI, and rotating the whole SVG element avoids every alignment quirk the other two builds carry.</p>
      </details>
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]">
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">B · borders</span>How does the zero-asset chevron work?
          <span aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa11-mut transition-[rotate,translate,border-color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:border-tfa11-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa11-acc motion-reduce:transition-colors"></span>
        </summary>
        <p class="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">A 9px square with only its right and bottom borders, pre-rotated 45° — so “rotate 180°” actually means <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">rotate-[225deg]</code>, and a 2px translate nudge re-centres its optical middle. Zero assets, two gotchas.</p>
      </details>
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]">
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">C · Unicode</span>When is a text arrow good enough?
          <span aria-hidden="true" class="ml-auto shrink-0 -translate-y-0.5 text-[18px] font-bold leading-none text-tfa11-mut transition-[rotate,translate,color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:text-tfa11-acc group-open:translate-y-0.5 group-open:rotate-180 group-open:text-tfa11-acc motion-reduce:transition-colors">&#8964;</span>
        </summary>
        <p class="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">Prototypes and plain-text contexts. It costs one character, but its baseline position depends on the font stack — explicit sizing and <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">leading-none</code> tame it, mostly. Ship SVG when it matters.</p>
      </details>
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]">
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">UX</span>Which way should a closed chevron point?
          <span aria-hidden="true" class="ml-auto grid shrink-0 place-items-center text-tfa11-mut transition-[rotate,color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:text-tfa11-acc group-open:rotate-180 group-open:text-tfa11-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="16" height="16"><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="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">Down when closed (content continues below), up when open. Right-pointing chevrons signal navigation into another screen — using one on an accordion misleads screen-magnifier and low-vision users especially. And match the rotation duration to the panel, or mismatched timings read as jank.</p>
      </details>
    </div>

    <p class="mt-6 text-[12.5px] text-tfa11-mut">All three rotate under <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">group-open:</code> — build B to 225°, because it starts at 45°.</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tfa11-bg: #f6f7fb;
  --color-tfa11-bg: oklch(0.97 0.008 250);
  --color-tfa11-ink: #262b38;
  --color-tfa11-ink: oklch(0.23 0.03 255);
  --color-tfa11-mut: #5e6472;
  --color-tfa11-mut: oklch(0.47 0.022 252);
  --color-tfa11-edge: #e2e5ee;
  --color-tfa11-edge: oklch(0.91 0.012 250);
  --color-tfa11-accedge: #8fa6e4;
  --color-tfa11-accedge: oklch(0.72 0.1 255);
  --color-tfa11-tint: #e9eefb;
  --color-tfa11-tint: oklch(0.94 0.028 255);
  --color-tfa11-acc: #2f4ec4;
  --color-tfa11-acc: oklch(0.47 0.19 260);
  --font-tfa11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa11-mono: 'JetBrains Mono', ui-monospace, monospace;
}

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

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

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

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

@media (prefers-reduced-motion: reduce) {
  .tfa-11 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-tfa11-bg: #f6f7fb;
  --color-tfa11-bg: oklch(0.97 0.008 250);
  --color-tfa11-ink: #262b38;
  --color-tfa11-ink: oklch(0.23 0.03 255);
  --color-tfa11-mut: #5e6472;
  --color-tfa11-mut: oklch(0.47 0.022 252);
  --color-tfa11-edge: #e2e5ee;
  --color-tfa11-edge: oklch(0.91 0.012 250);
  --color-tfa11-accedge: #8fa6e4;
  --color-tfa11-accedge: oklch(0.72 0.1 255);
  --color-tfa11-tint: #e9eefb;
  --color-tfa11-tint: oklch(0.94 0.028 255);
  --color-tfa11-acc: #2f4ec4;
  --color-tfa11-acc: oklch(0.47 0.19 260);
  --font-tfa11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa11-mono: 'JetBrains Mono', ui-monospace, monospace;
}

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

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

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

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

@media (prefers-reduced-motion: reduce) {
  .tfa-11 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.

  • The CSS original kept one shared --speed token so icon and panel could not drift. The utility version states duration-400 on both, which is more repetition but also more local — you see the timing on the element you are reading. If a project has many accordions, promote it back to --ease-*/duration tokens in @theme.
  • Three icon builds in one demo is where group-open: proves itself: one variant drives an SVG rotation, a 225° border rotation and a glyph rotation, with no per-build selector anywhere.
  • UI upgrade from the CSS original: muted copy oklch(0.5) → 0.47, accent 0.55 → 0.47; the chip labels now clear 4.5:1 at 10px, where the original's 10% tint chip did not.
  • UI upgrade: every icon now also responds to group-hover:, and motion-reduce: keeps the colour change instead of killing all feedback — the original's blanket duration override left reduced-motion users with no toggle confirmation at all.
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 FAQ Chevron Rotate 180 Degrees on Toggle
Source: https://codefronts.com/snippets/tailwind-css-faq-accordion/tailwind-faq-chevron-rotate-180-toggle/

The definitive treatment of the web's most-copied micro-interaction, ported: three chevron builds in one FAQ — inline SVG, a two-border CSS chevron, and a Unicode glyph — each rotating on group-open: with matched timing, plus the UX rules for which way a closed chevron should point and why the rotation duration must equal the panel's.
## HTML
```html
<section class="tfa-11 w-full min-h-svh grid place-items-center bg-tfa11-bg font-tfa11-sans text-tfa11-ink px-5 py-12">
  <div class="w-[min(680px,100%)]">
    <header>
      <h1 class="text-[clamp(28px,5vw,40px)] font-extrabold leading-[1.08] tracking-[-0.025em]">The 180° chevron, three ways</h1>
      <p class="mt-3 max-w-[56ch] text-[15px] leading-relaxed text-tfa11-mut">Same rotation, three icon builds — SVG, pure CSS borders, Unicode. Matched <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">duration-400</code> keeps icon and panel in sync.</p>
    </header>

    <div data-panel class="mt-8 flex flex-col gap-2.5">
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]" open>
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">A · SVG</span>Which icon build should production use?
          <span aria-hidden="true" class="ml-auto grid shrink-0 place-items-center text-tfa11-mut transition-[rotate,color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:text-tfa11-acc group-open:rotate-180 group-open:text-tfa11-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="16" height="16"><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="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">This one. <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">stroke: currentColor</code> inherits your text colour automatically, the rounded caps stay crisp at every DPI, and rotating the whole SVG element avoids every alignment quirk the other two builds carry.</p>
      </details>
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]">
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">B · borders</span>How does the zero-asset chevron work?
          <span aria-hidden="true" class="ml-auto size-2.5 shrink-0 -translate-y-0.5 rotate-45 border-b-2 border-r-2 border-tfa11-mut transition-[rotate,translate,border-color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:border-tfa11-acc group-open:translate-y-0.5 group-open:rotate-[225deg] group-open:border-tfa11-acc motion-reduce:transition-colors"></span>
        </summary>
        <p class="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">A 9px square with only its right and bottom borders, pre-rotated 45° — so “rotate 180°” actually means <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">rotate-[225deg]</code>, and a 2px translate nudge re-centres its optical middle. Zero assets, two gotchas.</p>
      </details>
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]">
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">C · Unicode</span>When is a text arrow good enough?
          <span aria-hidden="true" class="ml-auto shrink-0 -translate-y-0.5 text-[18px] font-bold leading-none text-tfa11-mut transition-[rotate,translate,color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:text-tfa11-acc group-open:translate-y-0.5 group-open:rotate-180 group-open:text-tfa11-acc motion-reduce:transition-colors">&#8964;</span>
        </summary>
        <p class="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">Prototypes and plain-text contexts. It costs one character, but its baseline position depends on the font stack — explicit sizing and <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">leading-none</code> tame it, mostly. Ship SVG when it matters.</p>
      </details>
      <details class="group rounded-2xl border border-tfa11-edge bg-white shadow-[0_2px_8px_oklch(0.4_0.05_250/0.05)] transition-[border-color,box-shadow] duration-300 open:border-tfa11-accedge open:shadow-[0_14px_34px_-20px_oklch(0.45_0.14_250/0.4)]">
        <summary class="flex min-h-11 cursor-pointer list-none items-center gap-3 px-5 py-4 text-[15px] font-semibold transition-colors duration-200 hover:text-tfa11-acc focus-visible:rounded-2xl focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-tfa11-acc [&::-webkit-details-marker]:hidden">
          <span class="shrink-0 rounded-md bg-tfa11-tint px-2 py-1 font-tfa11-mono text-[10px] font-bold tracking-[0.05em] text-tfa11-acc">UX</span>Which way should a closed chevron point?
          <span aria-hidden="true" class="ml-auto grid shrink-0 place-items-center text-tfa11-mut transition-[rotate,color] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] group-hover:text-tfa11-acc group-open:rotate-180 group-open:text-tfa11-acc motion-reduce:transition-colors"><svg viewBox="0 0 16 16" width="16" height="16"><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="max-w-[60ch] px-5 pb-4 text-[14px] leading-[1.7] text-tfa11-mut">Down when closed (content continues below), up when open. Right-pointing chevrons signal navigation into another screen — using one on an accordion misleads screen-magnifier and low-vision users especially. And match the rotation duration to the panel, or mismatched timings read as jank.</p>
      </details>
    </div>

    <p class="mt-6 text-[12.5px] text-tfa11-mut">All three rotate under <code class="rounded bg-tfa11-tint px-1.5 py-0.5 font-tfa11-mono text-[0.87em] text-tfa11-acc">group-open:</code> — build B to 225°, because it starts at 45°.</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tfa11-bg: #f6f7fb;
  --color-tfa11-bg: oklch(0.97 0.008 250);
  --color-tfa11-ink: #262b38;
  --color-tfa11-ink: oklch(0.23 0.03 255);
  --color-tfa11-mut: #5e6472;
  --color-tfa11-mut: oklch(0.47 0.022 252);
  --color-tfa11-edge: #e2e5ee;
  --color-tfa11-edge: oklch(0.91 0.012 250);
  --color-tfa11-accedge: #8fa6e4;
  --color-tfa11-accedge: oklch(0.72 0.1 255);
  --color-tfa11-tint: #e9eefb;
  --color-tfa11-tint: oklch(0.94 0.028 255);
  --color-tfa11-acc: #2f4ec4;
  --color-tfa11-acc: oklch(0.47 0.19 260);
  --font-tfa11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
  --font-tfa11-mono: 'JetBrains Mono', ui-monospace, monospace;
}

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

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

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

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

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

How this works

One variant, three icon builds. The SVG rotates as a whole element and is the production choice: stroke="currentColor" inherits the text colour, rounded caps stay crisp at any DPI, and there are no alignment quirks.

The CSS build is a 9px square with only right and bottom borders, pre-rotated 45°. That pre-rotation is why open means rotate-[225deg] rather than 180, and why it needs a 2px translate-y nudge to re-centre optically. The Unicode glyph costs one character but its baseline depends on the font stack — prototypes only.

Why the individual properties matter. Tailwind's rotate-* and translate-* compile to the individual rotate/translate properties in v4, so they compose instead of fighting — the classic transform overwrote my centring bug cannot happen here. Name them in the transition list and each keeps its own timing.

UX rules worth stealing: closed points down (content continues below), open points up. Right-pointing chevrons signal navigation into another screen, which misleads screen-magnifier users especially. And the rotation duration must equal the panel's height duration — a 200ms flick over a 400ms panel reads as broken, which is why both are duration-400 here.

Make it yours

  • A 270° twirl: group-open:rotate-[270deg] adds playfulness — keep it under 300ms or it upstages the content.
  • Colour shift on open: already wired (group-open:text-tfa11-acc) — state reads in peripheral vision.
  • Circle-chip chevron: wrap the SVG in a bordered size-8 ring (demo 02 does this) for a stronger hit-target affordance.
  • Timing: change the two duration-400 values together — icon and panel must agree.

Gotchas — read before shipping

  • Rotating a border-built chevron from 45° means going to 225°, not 180°. SVG avoids this entire class of bug.
  • Keep the chevron out of the text flow (ml-auto + shrink-0), or long questions wrap under it.
  • aria-hidden=&quot;true&quot; on the icon always — <details> already announces the state; a decorative arrow must not be read out.
  • transition-transform without a duration-* uses the 150ms default, which reads as a glitch.

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

Individual transform properties are 2022-settled everywhere; the rest is universal <details> plus transitions. The floor is Tailwind v4's generated sheet.

Techniques used in this demo

Search CodeFronts

Loading…