Skip to main content

Last updated: March 13, 2026

How to Create a CSS-Only Accordion (3 Methods)

Build accessible, animated accordions without JavaScript. Learn the details/summary approach, checkbox hack, and :target selector with interactive examples.

Why CSS-Only Accordions?

CSS-only accordions reduce JavaScript bundle size, work without scripting enabled, and can be more performant. They're perfect for FAQ sections, mobile menus, and collapsible content areas.

Details/Summary

Native HTML with built-in accessibility. Best for most use cases.

Checkbox Hack

Enables smooth CSS animations. Multiple items can be open.

:target Selector

URL-based state. Great for shareable deep links.

Pro tip: Use <details>/<summary> as your default choice. It's accessible, semantic, and works everywhere.

Quick Reference

Compare the three CSS-only accordion methods at a glance.

Details/SummaryBest

Browser Support:97%+
Animations:Yes
Accessible:Yes

Checkbox

Browser Support:99%+
Animations:Yes
Accessible:Needs ARIA

:target

Browser Support:99%+
Animations:Yes
Accessible:Needs ARIA
1

Details/Summary (Recommended)

Recommended

The native HTML approach using <details> and <summary> elements. Built-in accessibility, keyboard navigation, and toggle functionality without any CSS or JavaScript required.

Live Preview

What is Frontend Hero?+
Frontend Hero is an 11-in-1 browser extension for frontend developers that combines CSS inspection, color picking, font detection, and more.
How much does it cost?+
Frontend Hero is a one-time purchase of $59 with lifetime access. No subscription required.

Full Code

Pros

  • +Native HTML - no CSS required for basic functionality
  • +Built-in keyboard accessibility (Enter/Space to toggle)
  • +Screen reader friendly out of the box
  • +State preserved on page reload (with name attribute)
  • +Works without CSS or JavaScript

Cons

  • -Limited animation support (content appears instantly)
  • -Custom arrow styling requires extra CSS
  • -Cannot have multiple accordions open at once (without name attribute)

When to Use

  • *FAQ sections on websites
  • *Mobile navigation menus
  • *When accessibility is a priority
  • *Simple expand/collapse content
2

Checkbox Hack

Uses a hidden checkbox input with a label to toggle visibility. The CSS sibling selector (~) shows/hides content based on checkbox state.

Live Preview

This content uses the checkbox hack for smooth animation. The hidden checkbox controls visibility.

Full Code

Pros

  • +Smooth CSS animations possible
  • +Multiple items can be open simultaneously
  • +Excellent browser support (99%+)
  • +Checkbox state persists during session

Cons

  • -Not semantically correct HTML
  • -Requires unique IDs for each accordion
  • -Poor accessibility without ARIA attributes
  • -max-height animation requires known content height

When to Use

  • *When smooth animations are required
  • *Multiple accordions that can be open at once
  • *Legacy browser support needed
  • *When you're comfortable adding ARIA attributes
3

:target Selector

Uses URL hash fragments and the CSS :target pseudo-class. When you click a link with an anchor, the :target selector styles the matching element.

Live Preview

In a real implementation, clicking would add #section1 to the URL, and CSS :target would show this content.

The :target method is great for FAQ pages where you want shareable links to specific answers.

Full Code

Pros

  • +URL reflects accordion state (shareable links)
  • +Can deep-link to specific accordion items
  • +Pure CSS - no hidden inputs needed
  • +Can trigger CSS animations

Cons

  • -Changes browser URL (affects history)
  • -Only one accordion can be open at a time
  • -Page jumps to the accordion when clicked
  • -Requires close button or different UX for closing

When to Use

  • *FAQ pages where you want shareable links
  • *Documentation with deep-linkable sections
  • *Single-page applications with hash routing
  • *When URL state is beneficial

Styling Tips

Smooth Height Transitions

The max-height trick creates smooth open/close animations. Set a max-height larger than your content will ever be.

.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease-out;
}

.accordion.open .accordion-content {
  max-height: 500px; /* Larger than content */
}

Custom Arrow Icons

Replace the default arrow with CSS pseudo-elements or SVG icons for a custom look.

.accordion-header::after {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.3s ease;
}

details[open] .accordion-header::after {
  transform: rotate(-135deg);
}

Animation Timing

Use different easing functions for open vs close animations to feel more natural.

.accordion-content {
  transition: max-height 0.35s ease-out;
}

/* Faster close animation */
.accordion:not(.open) .accordion-content {
  transition: max-height 0.2s ease-in;
}

Grid-based Animation (Modern)

Use CSS Grid for true height animation without max-height hacks. Works in modern browsers.

.accordion-content {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.accordion.open .accordion-content {
  grid-template-rows: 1fr;
}

.accordion-content > div {
  overflow: hidden;
}

Accessibility Considerations

Use details/summary when possible

The <details> and <summary> elements have built-in accessibility. Screen readers announce them as expandable sections, and they work with keyboard navigation out of the box.

Add ARIA attributes for checkbox hack

If using the checkbox method, add role="button", aria-expanded, and aria-controls to make it accessible. Update aria-expanded with JavaScript when the state changes.

<div class="accordion">
  <input type="checkbox" id="acc1" class="accordion-checkbox" />
  <label
    for="acc1"
    class="accordion-header"
    role="button"
    aria-expanded="false"
    aria-controls="content1"
  >
    Click to expand
  </label>
  <div id="content1" class="accordion-content">
    Content here
  </div>
</div>

Ensure keyboard navigation

Users should be able to navigate to accordion headers with Tab and toggle them with Enter or Space. Native <details> handles this automatically.

Visible focus states

Always provide clear focus indicators for keyboard users. Never remove the outline without providing an alternative.

.accordion-header:focus {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

.accordion-header:focus:not(:focus-visible) {
  outline: none; /* Hide for mouse users */
}

Frequently Asked Questions

What's the best way to create an accordion without JavaScript?

The best method is using HTML <details> and <summary> elements. They provide built-in toggle functionality, keyboard accessibility, and screen reader support without any CSS or JavaScript. For smooth animations, the checkbox hack combined with max-height transitions is a good alternative.

How do I animate an accordion's height smoothly?

You can't directly animate height: auto in CSS. The common workaround is using max-height with a value larger than your content will ever be. For modern browsers, you can use CSS Grid with grid-template-rows transitioning from 0fr to 1fr, which gives true height animation without guessing max values.

Can I have only one accordion open at a time (exclusive accordion)?

Yes! With <details> elements, add the same name attribute to all items - browsers will automatically close others when one opens. For the checkbox method, use radio buttons instead of checkboxes with the same name. The :target method naturally supports this since only one hash can be active.

Are CSS-only accordions accessible?

The <details>/<summary> method is fully accessible by default - it supports keyboard navigation and screen readers. The checkbox and :target methods require additional ARIA attributes (aria-expanded, aria-controls, role='button') to be accessible. Always test with screen readers and keyboard navigation.

Why isn't my accordion animation working with height?

CSS cannot animate between height: 0 and height: auto. Use max-height instead, setting it to 0 when closed and a value larger than your content when open. The transition will animate the max-height property. For a more elegant solution, use CSS Grid with grid-template-rows.