25 CSS FAQ Accordions09 / 25

Pure CSSMIT licensed

Neumorphic Soft-UI FAQ Accordion Cards

Soft-UI done responsibly: FAQ cards extruded from a single-material surface using the twin-shadow trick (light source top-left, shade bottom-right), pressing physically inset when opened — the material metaphor doing the state communication. The demo bakes in the two fixes that make neumorphism shippable rather than a Dribbble liability: real borders on focus, and text contrast held at WCAG AA even though the palette is all one hue.

Published

Live Demo
Try it

The code

<section class="cfa-09" aria-label="Neumorphic soft-UI FAQ accordion">
  <div class="cfa-09__wrap">
    <header class="cfa-09__head">
      <h1>Device support</h1>
      <p>One material, one light source. Open a card and it presses into the surface.</p>
    </header>
    <div class="cfa-09__list">
      <details class="cfa-09__item" open>
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>How do I pair a new device?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>Hold the side button for 3 seconds until the ring pulses, then open the app — pairing is automatic within arm's reach. Up to 8 devices can stay paired to one account.</p></div>
      </details>
      <details class="cfa-09__item">
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>Why won't it charge past 80%?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>That is battery care mode, on by default — it doubles cell lifespan. Toggle “Full charge” in the app for trips; it reverts automatically after 48 hours.</p></div>
      </details>
      <details class="cfa-09__item">
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>Is it waterproof?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>IP68 — pool and shower safe, and rinsing after salt water is recommended. The charging contacts are corrosion-sealed, so no port covers to lose.</p></div>
      </details>
      <details class="cfa-09__item">
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>How do I factory reset?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>Settings → Device → Reset in the app, or hold both buttons for 10 seconds until three pulses. Your data stays in the cloud and restores on next pairing.</p></div>
      </details>
    </div>
    <p class="cfa-09__foot">Twin shadows out = raised · inset = pressed. Contrast held at AA despite the single-hue palette.</p>
  </div>
</section>
.cfa-09,
.cfa-09 *,
.cfa-09 *::before,
.cfa-09 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cfa-09 {
  --surface: oklch(0.92 0.018 250);
  --hi: color-mix(in oklch,var(--surface) 35%,white);
  --lo: color-mix(in oklch,var(--surface) 72%,oklch(0.55 0.06 260));
  --ink: oklch(0.3 0.04 260);
  --mut: oklch(0.45 0.035 258);
  --acc: oklch(0.55 0.16 262);
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(20px,4vw,56px);
  background: var(--surface);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.cfa-09__wrap {
  width: min(100%,600px);
}

.cfa-09__head h1 {
  font-size: clamp(26px,4.5cqi,36px);
  font-weight: 800;
  letter-spacing: -.02em;
}

.cfa-09__head p {
  margin-top: 8px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--mut);
}

.cfa-09__list {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  interpolate-size: allow-keywords;
}

.cfa-09__item {
  background: var(--surface);
  border-radius: 20px;
  box-shadow: -8px -8px 18px var(--hi),8px 8px 18px var(--lo);
  transition: box-shadow .35s cubic-bezier(.4,0,.2,1);
}

.cfa-09__item:not([open]):hover {
  box-shadow: -10px -10px 22px var(--hi),10px 10px 22px var(--lo);
}

.cfa-09__item[open] {
  box-shadow: inset -6px -6px 14px var(--hi),inset 6px 6px 14px var(--lo);
}

.cfa-09__q {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 44px;
  padding: 18px 20px;
  cursor: pointer;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.01em;
  list-style: none;
  border-radius: 20px;
  transition: translate .25s;
}

.cfa-09__q::-webkit-details-marker {
  display: none;
}

.cfa-09__item[open] .cfa-09__q {
  translate: 1px 1px;
}

.cfa-09__q:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.cfa-09__dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--acc);
  box-shadow: -2px -2px 4px var(--hi),2px 2px 4px var(--lo);
  transition: background .3s;
}

.cfa-09__chev {
  margin-left: auto;
  flex: none;
  width: 9px;
  height: 9px;
  border-right: 2.5px solid var(--mut);
  border-bottom: 2.5px solid var(--mut);
  border-radius: 1px;
  rotate: 45deg;
  translate: 0 -2px;
  transition: rotate .35s,border-color .3s;
}

.cfa-09__item[open] .cfa-09__chev {
  rotate: 225deg;
  translate: 0 2px;
  border-color: var(--acc);
}

.cfa-09__a p {
  padding: 0 20px 19px 42px;
  font-size: 13.5px;
  line-height: 1.75;
  color: var(--mut);
  max-width: 56ch;
}

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

  .cfa-09__item[open]::details-content {
    height: auto;
    opacity: 1;
  }
}

.cfa-09__foot {
  margin-top: 24px;
  font-size: 12px;
  color: var(--mut);
}

@media (prefers-reduced-motion: reduce) {
  .cfa-09 *,
    .cfa-09 .cfa-09__item::details-content {
    transition-duration: .01ms !important;
  }
}
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 FAQ Accordion 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: Neumorphic Soft-UI FAQ Accordion Cards
Source: https://codefronts.com/snippets/css-faq-accordion/neumorphic-soft-ui-faq-accordion-cards/

Soft-UI done responsibly: FAQ cards extruded from a single-material surface using the twin-shadow trick (light source top-left, shade bottom-right), pressing physically inset when opened — the material metaphor doing the state communication. The demo bakes in the two fixes that make neumorphism shippable rather than a Dribbble liability: real borders on focus, and text contrast held at WCAG AA even though the palette is all one hue.
## HTML
```html
<section class="cfa-09" aria-label="Neumorphic soft-UI FAQ accordion">
  <div class="cfa-09__wrap">
    <header class="cfa-09__head">
      <h1>Device support</h1>
      <p>One material, one light source. Open a card and it presses into the surface.</p>
    </header>
    <div class="cfa-09__list">
      <details class="cfa-09__item" open>
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>How do I pair a new device?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>Hold the side button for 3 seconds until the ring pulses, then open the app — pairing is automatic within arm's reach. Up to 8 devices can stay paired to one account.</p></div>
      </details>
      <details class="cfa-09__item">
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>Why won't it charge past 80%?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>That is battery care mode, on by default — it doubles cell lifespan. Toggle “Full charge” in the app for trips; it reverts automatically after 48 hours.</p></div>
      </details>
      <details class="cfa-09__item">
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>Is it waterproof?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>IP68 — pool and shower safe, and rinsing after salt water is recommended. The charging contacts are corrosion-sealed, so no port covers to lose.</p></div>
      </details>
      <details class="cfa-09__item">
        <summary class="cfa-09__q"><span class="cfa-09__dot" aria-hidden="true"></span>How do I factory reset?<i class="cfa-09__chev" aria-hidden="true"></i></summary>
        <div class="cfa-09__a"><p>Settings → Device → Reset in the app, or hold both buttons for 10 seconds until three pulses. Your data stays in the cloud and restores on next pairing.</p></div>
      </details>
    </div>
    <p class="cfa-09__foot">Twin shadows out = raised · inset = pressed. Contrast held at AA despite the single-hue palette.</p>
  </div>
</section>
```
## CSS
```css
.cfa-09,
.cfa-09 *,
.cfa-09 *::before,
.cfa-09 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cfa-09 {
  --surface: oklch(0.92 0.018 250);
  --hi: color-mix(in oklch,var(--surface) 35%,white);
  --lo: color-mix(in oklch,var(--surface) 72%,oklch(0.55 0.06 260));
  --ink: oklch(0.3 0.04 260);
  --mut: oklch(0.45 0.035 258);
  --acc: oklch(0.55 0.16 262);
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(20px,4vw,56px);
  background: var(--surface);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
}

.cfa-09__wrap {
  width: min(100%,600px);
}

.cfa-09__head h1 {
  font-size: clamp(26px,4.5cqi,36px);
  font-weight: 800;
  letter-spacing: -.02em;
}

.cfa-09__head p {
  margin-top: 8px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--mut);
}

.cfa-09__list {
  margin-top: 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  interpolate-size: allow-keywords;
}

.cfa-09__item {
  background: var(--surface);
  border-radius: 20px;
  box-shadow: -8px -8px 18px var(--hi),8px 8px 18px var(--lo);
  transition: box-shadow .35s cubic-bezier(.4,0,.2,1);
}

.cfa-09__item:not([open]):hover {
  box-shadow: -10px -10px 22px var(--hi),10px 10px 22px var(--lo);
}

.cfa-09__item[open] {
  box-shadow: inset -6px -6px 14px var(--hi),inset 6px 6px 14px var(--lo);
}

.cfa-09__q {
  display: flex;
  align-items: center;
  gap: 13px;
  min-height: 44px;
  padding: 18px 20px;
  cursor: pointer;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.01em;
  list-style: none;
  border-radius: 20px;
  transition: translate .25s;
}

.cfa-09__q::-webkit-details-marker {
  display: none;
}

.cfa-09__item[open] .cfa-09__q {
  translate: 1px 1px;
}

.cfa-09__q:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.cfa-09__dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--acc);
  box-shadow: -2px -2px 4px var(--hi),2px 2px 4px var(--lo);
  transition: background .3s;
}

.cfa-09__chev {
  margin-left: auto;
  flex: none;
  width: 9px;
  height: 9px;
  border-right: 2.5px solid var(--mut);
  border-bottom: 2.5px solid var(--mut);
  border-radius: 1px;
  rotate: 45deg;
  translate: 0 -2px;
  transition: rotate .35s,border-color .3s;
}

.cfa-09__item[open] .cfa-09__chev {
  rotate: 225deg;
  translate: 0 2px;
  border-color: var(--acc);
}

.cfa-09__a p {
  padding: 0 20px 19px 42px;
  font-size: 13.5px;
  line-height: 1.75;
  color: var(--mut);
  max-width: 56ch;
}

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

  .cfa-09__item[open]::details-content {
    height: auto;
    opacity: 1;
  }
}

.cfa-09__foot {
  margin-top: 24px;
  font-size: 12px;
  color: var(--mut);
}

@media (prefers-reduced-motion: reduce) {
  .cfa-09 *,
    .cfa-09 .cfa-09__item::details-content {
    transition-duration: .01ms !important;
  }
}
```

How this works

Neumorphism is one lighting model: everything is the same material as the background, shaped only by light from a consistent corner. Two shadows fake the extrusion —

.card{
  background:var(--surface);
  border-radius:20px;
  box-shadow:-8px -8px 18px var(--hi),   /* light, top-left  */
              8px  8px 18px var(--lo);   /* shade, bottom-right */
}
.card[open]{
  box-shadow:inset -6px -6px 14px var(--hi),
             inset  6px  6px 14px var(--lo);  /* pressed in */
}

The open state inverts to inset shadows — the card is pressed into the surface, a physical metaphor users decode instantly with no icon required (the chevron here is a garnish, not the signal). Both shadow colors derive from the surface with color-mix, so re-theming stays a one-token edit:

--surface:oklch(0.92 0.02 250);
--hi:color-mix(in oklch, var(--surface) 40%, white);
--lo:color-mix(in oklch, var(--surface) 75%, oklch(0.55 0.05 260));

And the honesty section, because your users aren't Dribbble judges: pure neumorphism has famously weak affordances (everything is the same color) and fails focus-visibility. The fixes here — ink at oklch 0.3 for 7:1 contrast, a real 2px outline on :focus-visible, and hover raising the extrusion by 2px so pointers get feedback — cost nothing visually and make the style production-legal.

Make it yours

  • Material: change --surface once; both shadows recompute via color-mix. Cool gray, warm sand and sage all read well — avoid saturated surfaces.
  • Depth: the 8px/18px offset/blur pair is the extrusion height; halve both for subtle, double for chunky toy-UI.
  • Light direction: to move the light source top-right, swap the signs on the x offsets in both shadow pairs — consistency across ALL elements is the one hard rule of the style.
  • Pressed content: the inner padding shifts 1px down-right when open, selling the press — remove the two translate lines if too cute.

Gotchas — read before shipping

  • Neumorphism needs a mid-lightness surface (oklch 0.88–0.94): on white the highlight vanishes, on dark the shade does — the style physically cannot work at the extremes.
  • Never communicate state by shadow alone for critical UI — this demo keeps the chevron and the aria semantics of details; the press is reinforcement, not the only signal.
  • Soft shadows stack blur fast: 6 cards × 4 shadows is fine, but avoid animating box-shadow on scroll-linked effects — transition only on state change, as here.
  • Skip inset shadows on the text itself (embossed text) — it fails contrast the moment it looks good.

Browser support

ChromeSafariFirefoxEdge
129+not yetnot yet129+

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

Box-shadow twin-lighting is universal; the floor is oklch/color-mix token math. Height animation via ::details-content enhances progressively; panels snap open elsewhere.

Techniques used in this demo

Search CodeFronts

Loading…