How to Create an Accordion in CSS

Build a collapsible accordion that reveals one panel of content at a time when its header is clicked.

What an accordion is for

An accordion is a stack of headers, each attached to a panel of content. Clicking a header expands or collapses the panel beneath it - useful whenever you have several chunks of related content (an FAQ, a settings list, a set of steps) and you want the page to stay short until the visitor asks to see more. There are two solid ways to build one: the zero-JavaScript native `<details>`/`<summary>` elements, or a `<button>` + `classList.toggle()` pattern when you need more control over styling and behavior, like closing other panels automatically.

Option 1: the native <details> element

The simplest accordion needs no JavaScript at all. Wrap each section in a `<details>` element and put the clickable header in a `<summary>` child. The browser handles the open/close state, the click handling, and even keyboard access for free.

Example: no-JS accordion with <details>

<!DOCTYPE html>
<html>
<head>
<style>
  details {
    border: 1px solid #d9dde3;
    border-radius: 8px;
    margin-bottom: 8px;
    padding: 0.5rem 1rem;
  }
  summary {
    cursor: pointer;
    font-weight: 600;
    list-style: none; /* hide the default triangle marker */
  }
  summary::-webkit-details-marker {
    display: none; /* Safari/Chrome default marker */
  }
  summary::after {
    content: "+";
    float: right;
  }
  details[open] summary::after {
    content: "\2212"; /* minus sign when open */
  }
</style>
</head>
<body>

<details>
  <summary>What is an accordion?</summary>
  <p>A UI pattern that hides content behind a clickable header, showing one
  or more panels at a time.</p>
</details>

<details>
  <summary>Do I need JavaScript?</summary>
  <p>No. The &lt;details&gt; element toggles itself. JavaScript is only
  needed for extra behavior, like closing other panels automatically.</p>
</details>

</body>
</html>
Note: Give every <details> in the same accordion the same `name` attribute (a newer HTML feature) and the browser will automatically close the others when one opens - no JavaScript required for exclusive accordions either.

Option 2: a custom accordion with classList.toggle

When you want full control over the animation, icons, or which panels can stay open together, build the accordion from a plain `<button>` header and a sibling content panel. Clicking the button toggles an `is-open` class on itself and on the panel; CSS reacts to that class to show or hide the content and rotate the icon.

Example: custom accordion with JavaScript

<!DOCTYPE html>
<html>
<head>
<style>
  .accordion-item { border-bottom: 1px solid #e2e5ea; }
  .accordion-header {
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    padding: 1rem 0;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .accordion-header .icon {
    transition: transform 0.2s ease;
  }
  .accordion-header.is-open .icon {
    transform: rotate(45deg); /* turns a "+" into an "x" */
  }
  .accordion-panel {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s ease;
  }
  .accordion-panel.is-open {
    max-height: 200px; /* large enough to fit the content */
  }
  .accordion-panel p { margin: 0 0 1rem; }
</style>
</head>
<body>

<div class="accordion-item">
  <button class="accordion-header">
    Shipping details
    <span class="icon">+</span>
  </button>
  <div class="accordion-panel">
    <p>Orders ship within two business days via standard courier.</p>
  </div>
</div>

<script>
  document.querySelectorAll('.accordion-header').forEach(function (header) {
    header.addEventListener('click', function () {
      var panel = header.nextElementSibling;
      header.classList.toggle('is-open');
      panel.classList.toggle('is-open');
    });
  });
</script>

</body>
</html>

The `max-height` trick is what makes the panel animate smoothly: you cannot transition `height: auto`, but you can transition `max-height` from 0 to a value larger than the content will ever need. The panel is clipped with `overflow: hidden` so anything beyond its current max-height stays invisible.

  • Use a <button> for the header, not a <div> - buttons are focusable and clickable with the keyboard (Enter/Space) out of the box.
  • Toggle a class rather than inline styles, so all the visual states live together in your CSS.
  • If you want only one panel open at a time, close every other panel's classes before toggling the clicked one.
  • Add aria-expanded="true|false" to the header button and toggle it alongside the class, so screen readers announce whether the panel is currently open.
ApproachJavaScript needed?Best for
<details>/<summary>NoSimple FAQs, quick content reveals
classList.toggle + CSSYes (a few lines)Custom animation, icons, exclusive-open logic

Exercise: Interactive Components

Which statement about building a dropdown toggle is accurate?

Frequently Asked Questions

How do I make an accordion without JavaScript?
Use <details> with a <summary> heading. The browser handles opening and closing, keyboard access and screen reader semantics for free. Style the marker with summary::marker or hide it and supply your own icon.
How do I animate an accordion opening?
Transition grid-template-rows from 0fr to 1fr on a wrapper, or animate max-height to a value larger than the content. Height itself cannot be transitioned from auto, which is why naive height animations do nothing.