Home / Snippets / UI Components /

CSS accordion

A smooth expanding accordion built with the HTML details/summary elements — no JavaScript, fully accessible, animated with CSS transitions.

What is CSS-only accordion?
It uses the native <details> and <summary> HTML elements. This provides built-in accessibility and state management without writing a single line of JavaScript.
Is it accessible?
Yes. Browsers automatically handle keyboard navigation (Enter/Space to toggle) and ARIA attributes (aria-expanded) for these elements, making them screen reader friendly by default.
Can I animate it?
Absolutely. We use the CSS Grid grid-template-rows trick. By transitioning from 0fr to 1fr, we can animate the height smoothly without needing max-height hacks.
Widely Supported
uino-js

Quick implementation

<!-- HTML Structure -->
<details class="accordion-item">
  <summary class="accordion-summary">
    <span>Title Here</span>
    <span class="accordion-icon">▸</span>
  </summary>
  <div class="accordion-content">
    <div class="accordion-inner">
      <div class="accordion-body">
        Content goes here...
      </div>
    </div>
  </div>
</details>

/* CSS Styling */
.accordion-container {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  overflow: hidden;
}

.accordion-item {
  border-bottom: 1px solid var(--card-border);
  background-color: var(--card);
}

.accordion-item[open] {
  background-color: oklch(0.18 0.02 240);
}

.accordion-summary {
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  cursor: pointer;
  font-weight: 600;
}

.accordion-summary::-webkit-details-marker {
  display: none;
}

.accordion-icon {
  transition: transform 0.3s ease;
}

.accordion-item[open] .accordion-icon {
  transform: rotate(90deg);
}

/* The Grid Animation Trick */
.accordion-content {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.accordion-inner {
  overflow: hidden;
}

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

@media (prefers-reduced-motion: reduce) {
  .accordion-content,
  .accordion-icon {
    transition: none;
  }
}

Prompt this to your LLM

Includes role, constraints, and framework variants.

Role: Accessibility-first frontend engineer.

Goal: Create a CSS-only accordion using native HTML elements.

Constraints:
1. Use <details> and <summary> for semantics.
2. Animate height using grid-template-rows (0fr to 1fr).
3. Use oklch() for all color values.
4. Ensure focus-visible outlines are present.
5. No JavaScript for logic or animation.

Variants:
A) Vanilla CSS: Use attribute selectors (details[open]).
B) React: Use a wrapper component that passes className.

Edge Cases:
1. Nested accordions (ensure z-index or borders handle correctly).
2. prefers-reduced-motion (disable transitions).
3. Safari support for grid-template-rows animation.

Why this matters in 2026

The <details> element is the ultimate "progressive enhancement" tool. It works out of the box in every browser, requires zero JavaScript, and handles all the complex ARIA logic for us. In 2026, we are moving away from heavy JavaScript libraries for simple UI patterns. The only hurdle was the animation, which is now solved elegantly by the CSS Grid grid-template-rows transition.

The logic

The magic lies in the .accordion-content class. By setting display: grid and grid-template-rows: 0fr, the content is rendered but has zero height. When the details element is open, we change the grid row to 1fr. The browser interpolates the height automatically. We wrap the actual content in .accordion-inner with overflow: hidden to ensure the content doesn't spill out during the transition.

Accessibility & performance

Performance is excellent because the browser handles the state and the rendering. Accessibility is native: screen readers announce the expanded/collapsed state automatically. Keyboard users can tab to the summary and press Enter or Space to toggle. We wrap the transition in a prefers-reduced-motion media query to respect user preferences.