SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/accordions

8 CSS Accordions

6 Pure CSS2 with JSPublished 2026-10-04Free · no sign-up

Eight accordions for FAQs, product details and navigation: a native details accordion, a one-at-a-time group with the name attribute, smooth height animation, the ARIA button pattern, numbered FAQ cards, horizontal gallery panels, a nested docs menu and Expand all controls.

01. Details and Summary Accordion

NativeNo JS

An accordion built from details and summary elements, so expanding, collapsing and keyboard support come from the browser. The default marker is replaced by a rotating plus sign, and each summary is a heading-sized label for easy scanning.

View code
<div class="acc">
  <details open><summary>How long does delivery take?</summary><p>Standard delivery takes 3 to 5 working days. Express arrives the next working day if you order before 2 pm.</p></details>
  <details><summary>Can I return an item?</summary><p>Yes, within 30 days if it is unused and in its original packaging.</p></details>
  <details><summary>Do you ship abroad?</summary><p>We ship to 24 countries. Duties are shown at checkout.</p></details>
</div>
.acc { width: min(440px, 92vw); border-radius: 14px; background: #fff; box-shadow: 0 8px 26px -14px rgba(16,24,40,.3); font-family: Inter, system-ui, sans-serif; }
.acc details { border-bottom: 1px solid #eef0f4; }
.acc details:last-child { border-bottom: 0; }
.acc summary { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 16px 18px; color: #101828; font-weight: 600; cursor: pointer; list-style: none; }
.acc summary::-webkit-details-marker { display: none; }
.acc summary::after { content: "+"; color: #2563eb; font-size: 22px; font-weight: 400; line-height: 1; transition: transform .2s; }
.acc details[open] summary::after { transform: rotate(45deg); }
.acc summary:focus-visible { outline: 2px solid #2563eb; outline-offset: -2px; border-radius: 10px; }
.acc p { margin: 0; padding: 0 18px 16px; color: #475467; font-size: 14.5px; line-height: 1.6; }

02. Exclusive Accordion with name

NativeModern

Only one panel open at a time, with no JavaScript: details elements that share the same name attribute behave like a group, so opening one closes the others. Older browsers simply allow several open at once, which is a safe fallback.

View code
<div class="ex">
  <details name="faq" open><summary>What is included in the plan?</summary><p>Unlimited products, a custom domain and email support.</p></details>
  <details name="faq"><summary>Can I cancel any time?</summary><p>Yes. You keep access until the end of the billing period.</p></details>
  <details name="faq"><summary>Is there a free trial?</summary><p>Fourteen days, no card needed.</p></details>
</div>
.ex { display: grid; gap: 8px; width: min(440px, 92vw); font-family: Inter, system-ui, sans-serif; }
.ex details { border: 1px solid #262d3e; border-radius: 12px; background: #12161f; }
.ex details[open] { border-color: #7c9cff; }
.ex summary { padding: 14px 16px; color: #fff; font-weight: 600; cursor: pointer; list-style: none; }
.ex summary::-webkit-details-marker { display: none; }
.ex summary::before { content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 12px; border: solid #7c9cff; border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform .2s; vertical-align: 2px; }
.ex details[open] summary::before { transform: rotate(45deg); }
.ex summary:focus-visible { outline: 2px solid #7c9cff; outline-offset: 2px; border-radius: 12px; }
.ex p { margin: 0; padding: 0 16px 16px 35px; color: #aab3c7; font-size: 14.5px; line-height: 1.6; }

03. Smoothly Animated Accordion

AnimationModern

Panels that open with a smooth height animation, made possible by interpolate-size: allow-keywords and the ::details-content pseudo-element, which let CSS animate to height: auto. Browsers without support open the panel instantly instead.

View code
<div class="an">
  <details><summary>Shipping and returns</summary><div class="c"><p>Free shipping over $60. Returns are free within 30 days, and we email a prepaid label.</p></div></details>
  <details><summary>Materials</summary><div class="c"><p>Organic cotton canvas with leather handles from a certified tannery.</p></div></details>
</div>
:root { interpolate-size: allow-keywords; }
.an { width: min(440px, 92vw); font-family: Inter, system-ui, sans-serif; }
.an details { border-top: 1px solid #d0d5dd; }
.an details:last-child { border-bottom: 1px solid #d0d5dd; }
.an summary { display: flex; justify-content: space-between; padding: 16px 2px; color: #101828; font-weight: 700; cursor: pointer; list-style: none; }
.an summary::-webkit-details-marker { display: none; }
.an summary::after { content: ""; width: 8px; height: 8px; border: solid #101828; border-width: 0 2px 2px 0; transform: rotate(45deg); transition: transform .3s; }
.an details[open] summary::after { transform: rotate(-135deg); }
.an details::details-content { height: 0; overflow: hidden; transition: height .35s ease, content-visibility .35s allow-discrete; }
.an details[open]::details-content { height: auto; }
.an p { margin: 0 2px 16px; color: #475467; line-height: 1.6; }
.an summary:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .an details::details-content, .an summary::after { transition: none; } }

04. ARIA Button Accordion

ARIAJavaScript

The classic ARIA accordion pattern for when you need full control: each header is a heading containing a button with aria-expanded and aria-controls, and each panel is a region labelled by its button. The script only toggles attributes and hidden.

View code
<div class="aa">
  <h3><button aria-expanded="true" aria-controls="aa1" id="ab1">Account</button></h3>
  <div id="aa1" role="region" aria-labelledby="ab1"><p>Change your email, password and two-step sign-in.</p></div>
  <h3><button aria-expanded="false" aria-controls="aa2" id="ab2">Billing</button></h3>
  <div id="aa2" role="region" aria-labelledby="ab2" hidden><p>Update your card and download invoices.</p></div>
  <h3><button aria-expanded="false" aria-controls="aa3" id="ab3">Notifications</button></h3>
  <div id="aa3" role="region" aria-labelledby="ab3" hidden><p>Choose which emails and alerts you receive.</p></div>
</div>
.aa { width: min(440px, 92vw); border: 1px solid #262d3e; border-radius: 14px; background: #12161f; font-family: Inter, system-ui, sans-serif; }
.aa h3 { margin: 0; border-top: 1px solid #262d3e; }
.aa h3:first-child { border-top: 0; }
.aa button { display: flex; justify-content: space-between; width: 100%; padding: 15px 16px; border: 0; background: none; color: #fff; font: 600 15px Inter, sans-serif; text-align: left; cursor: pointer; }
.aa button::after { content: "\25BE"; color: #34d399; transition: transform .2s; }
.aa button[aria-expanded=true]::after { transform: rotate(180deg); }
.aa button:focus-visible { outline: 2px solid #34d399; outline-offset: -2px; }
.aa [role=region] p { margin: 0; padding: 0 16px 16px; color: #aab3c7; font-size: 14.5px; line-height: 1.6; }
document.querySelectorAll('.aa button').forEach(b => b.addEventListener('click', () => {
  const open = b.getAttribute('aria-expanded') !== 'true';
  b.setAttribute('aria-expanded', String(open));
  document.getElementById(b.getAttribute('aria-controls')).hidden = !open;
}));

05. Card-Style FAQ Accordion

FAQCards

FAQ items as separate rounded cards with a numbered badge, the style often used on landing pages. The open card gains a tinted background and a shadow, which makes the active question easy to spot when scrolling a long list.

View code
<div class="cq">
  <details open><summary><span>01</span>Do I need coding skills?</summary><p>No. Templates come ready to edit with a visual editor, and every block has sensible defaults.</p></details>
  <details><summary><span>02</span>Can I use my own domain?</summary><p>Yes, on every paid plan. We walk you through the DNS settings.</p></details>
  <details><summary><span>03</span>How do refunds work?</summary><p>Ask within 14 days for a full refund, no questions asked.</p></details>
</div>
.cq { display: grid; gap: 10px; width: min(460px, 94vw); font-family: Inter, system-ui, sans-serif; }
.cq details { border: 1px solid #e4e7ec; border-radius: 16px; background: #fff; transition: background .2s, box-shadow .2s; }
.cq details[open] { background: #f5f3ff; box-shadow: 0 10px 24px -14px rgba(109,40,217,.4); border-color: #ddd6fe; }
.cq summary { display: flex; align-items: center; gap: 12px; padding: 16px 18px; color: #101828; font-weight: 700; cursor: pointer; list-style: none; }
.cq summary::-webkit-details-marker { display: none; }
.cq summary span { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 9px; background: #ede9fe; color: #5b21b6; font: 800 12px "JetBrains Mono", monospace; }
.cq summary:focus-visible { outline: 2px solid #6d28d9; outline-offset: 2px; border-radius: 16px; }
.cq p { margin: 0; padding: 0 18px 18px 60px; color: #475467; line-height: 1.6; font-size: 14.5px; }

06. Horizontal Accordion Panels

GalleryFlex

Panels laid out side by side that widen when chosen, a gallery-style accordion. Each panel is a radio-backed label, so only one is wide at a time and arrow keys move between them, with the grow effect made by changing flex-grow.

View code
<fieldset class="hz">
  <legend class="sr">Destinations</legend>
  <label style="--c:#0ea5e9"><input type="radio" name="hz" checked><b>Coast</b><span>Cliff walks and quiet coves.</span></label>
  <label style="--c:#22c55e"><input type="radio" name="hz"><b>Forest</b><span>Cabins under old oaks.</span></label>
  <label style="--c:#f59e0b"><input type="radio" name="hz"><b>Desert</b><span>Clear skies, cold nights.</span></label>
  <label style="--c:#a855f7"><input type="radio" name="hz"><b>City</b><span>Galleries and late dinners.</span></label>
</fieldset>
.hz { box-sizing: border-box; max-width: 100%; min-inline-size: 0; display: flex; gap: 8px; width: min(560px, 94vw); height: 240px; margin: 0; padding: 0; border: 0; font-family: Inter, system-ui, sans-serif; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.hz label { position: relative; flex: 1; display: flex; flex-direction: column; justify-content: flex-end; gap: 4px; min-width: 0; padding: 16px; overflow: hidden; border-radius: 16px; background: linear-gradient(180deg, transparent 30%, rgba(0,0,0,.7)), var(--c); color: #fff; cursor: pointer; transition: flex-grow .4s; }
.hz input { position: absolute; opacity: 0; }
.hz b { white-space: nowrap; font-size: 17px; }
.hz span { font-size: 13px; opacity: 0; white-space: nowrap; transition: opacity .3s; }
.hz label:has(input:checked) { flex-grow: 4; }
.hz label:has(input:checked) span { opacity: 1; }
.hz label:has(input:focus-visible) { outline: 2px solid #fff; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .hz label, .hz span { transition: none; } }

07. Nested Accordion Menu

NavigationTree

A documentation sidebar with sections that expand to show pages, and a sub-section nested inside. Nested details elements create the tree without JavaScript, indentation shows the depth, and the current page is marked with aria-current.

View code
<nav class="nst" aria-label="Documentation">
  <details open><summary>Getting started</summary>
    <ul><li><a href="#">Install</a></li><li><a href="#" aria-current="page">First project</a></li>
      <li><details><summary>Configuration</summary><ul><li><a href="#">Themes</a></li><li><a href="#">Plugins</a></li></ul></details></li>
    </ul>
  </details>
  <details><summary>Guides</summary><ul><li><a href="#">Deploying</a></li><li><a href="#">Testing</a></li></ul></details>
</nav>
.nst { width: 240px; padding: 10px; border-radius: 14px; background: #fff; box-shadow: 0 8px 24px -12px rgba(16,24,40,.25); font: 14px Inter, system-ui, sans-serif; }
.nst summary { padding: 7px 8px; border-radius: 8px; color: #101828; font-weight: 700; cursor: pointer; }
.nst summary:hover { background: #f2f4f7; }
.nst summary:focus-visible { outline: 2px solid #2563eb; }
.nst ul { margin: 2px 0 6px 10px; padding-left: 10px; list-style: none; border-left: 1px solid #e4e7ec; }
.nst ul summary { font-weight: 600; color: #344054; }
.nst a { display: block; padding: 6px 8px; border-radius: 6px; color: #475467; text-decoration: none; }
.nst a:hover { background: #f2f4f7; color: #101828; }
.nst a[aria-current] { background: #eef4ff; color: #1d4ed8; font-weight: 600; }
.nst a:focus-visible { outline: 2px solid #2563eb; }

08. Accordion with Expand All

JavaScriptLong list

A long policy page benefits from Expand all and Collapse all controls. The buttons set the open property on every details element, the button label switches to match, and each item still works on its own for people who want just one answer.

View code
<div class="ea">
  <button id="ea-t" aria-controls="ea-l">Expand all</button>
  <div id="ea-l">
    <details><summary>Data we collect</summary><p>Your name, email and order history, only to run your account.</p></details>
    <details><summary>How long we keep it</summary><p>For as long as your account is open, then 12 months for tax records.</p></details>
    <details><summary>Your rights</summary><p>You can download or delete your data from account settings.</p></details>
  </div>
</div>
.ea { display: grid; gap: 10px; width: min(440px, 92vw); font-family: Inter, system-ui, sans-serif; }
#ea-t { justify-self: end; padding: 7px 12px; border: 1px solid #343c51; border-radius: 8px; background: none; color: #c9d1e2; font: 600 13px Inter, sans-serif; cursor: pointer; }
#ea-t:focus-visible { outline: 2px solid #f472b6; }
#ea-l details { border-bottom: 1px solid #262d3e; }
#ea-l summary { padding: 13px 4px; color: #fff; font-weight: 600; cursor: pointer; }
#ea-l summary:focus-visible { outline: 2px solid #f472b6; }
#ea-l p { margin: 0 4px 14px; color: #aab3c7; font-size: 14px; line-height: 1.6; }
const t = document.getElementById('ea-t'), all = [...document.querySelectorAll('#ea-l details')];
function label() { t.textContent = all.every(d => d.open) ? 'Collapse all' : 'Expand all'; }
t.addEventListener('click', () => { const open = !all.every(d => d.open); all.forEach(d => d.open = open); label(); });
all.forEach(d => d.addEventListener('toggle', label));

About these CSS Accordions

details and summary first

<details> and <summary> give you expand and collapse, keyboard support and screen reader announcements with no JavaScript. Replace the default triangle by hiding ::-webkit-details-marker and adding your own icon.

One open at a time

Give several details elements the same name attribute and the browser closes the others when one opens. Older browsers ignore it and allow several open, which is a harmless fallback.

Animating to auto height

interpolate-size: allow-keywords together with the ::details-content pseudo-element lets CSS animate a panel to height: auto. Where unsupported, panels open instantly.

FAQ structured data

If an accordion holds genuine questions and answers, you can describe them with FAQPage structured data. Search engines now show FAQ rich results only for some sites, so treat it as context, not a guaranteed result.

Frequently asked questions

Should I use details or ARIA buttons?

Use details for most content. Use the ARIA button pattern when you need custom behaviour or headings around the buttons.

Is content in a closed accordion indexed?

Search engines can read text in closed details elements, though visible text is generally safest for key information.

Which demos need JavaScript?

The ARIA accordion and the Expand all demo. The rest are HTML and CSS.