---
title: "How to build an accordion with HTML and CSS without JavaScript: the details element"
description: "How to build an accordion with HTML and CSS without JavaScript: the details element, the name attribute for one open item, a custom arrow, smooth opening and browser support."
url: https://penlab.space/blog/accordion-details-html-css
language: en
section: "Articles"
published: 2026-10-12T15:56:43Z
updated: 2026-10-12T15:56:43Z
translation: https://penlab.space/blog/ru/accordion-details-html-css
---

# How to build an accordion with HTML and CSS without JavaScript: the details element

An accordion is built on the details element: the browser opens the items, and the name attribute keeps only one open. We show the code, a custom arrow, smooth opening in CSS and browser support.

An accordion in HTML and CSS needs no JavaScript: it is built on the `details` element. The item heading goes into `summary`, the hidden text follows it, and the browser opens the item itself. To keep only one item open at a time, we give every `details` of the group the same `name` attribute (Chrome and Edge 120, Firefox 130, Safari 17.2). Smooth opening is added by CSS with `::details-content`, but only Chrome and Edge can animate the `auto` height so far: in other browsers the item opens at once.

## In short

- An accordion item is a `details` whose first child is `summary`. A click on `summary` opens and closes the item, no script needed.
- An item is open while `details` has the `open` attribute. It does not touch other items.
- The same `name` on several `details` joins them into a group: opening one closes another. The group does not require one item to be open all the time: a click on the open item closes it.
- We remove the standard arrow with `list-style: none` and `::-webkit-details-marker`, and draw our own in `::after` on `summary`.
- An item works from the keyboard: Tab moves to `summary`, Enter and Space open and close it. We checked this in Chrome 154.
- Smooth opening comes from `::details-content` (Chrome and Edge 131, Firefox 143, Safari 18.4) together with `interpolate-size`, which only Chrome and Edge understand. Without it the accordion works the same, only without animation.

## How to build an accordion on details

An accordion is a list of items, each with a heading, and the text under it stays hidden until the item is open. HTML already has such an item: the `details` element hides everything except `summary` and shows it after a click on the heading.

```html
<details>
  <summary>What is an accordion?</summary>
  <p>A list of items, each with a heading and hidden text.</p>
</details>

<details>
  <summary>Do we need JavaScript?</summary>
  <p>No, the browser opens the items itself.</p>
</details>
```

We put `summary` first inside `details`. Without it the browser shows its own label, usually "Details". Everything else inside `details` is ordinary markup: paragraphs, lists, links, forms.

An item is open while `details` has the boolean attribute `open`. To make an item open on page load we write `<details open>`. The browser adds and removes this attribute itself when the user clicks the heading. To close an item in the markup we remove the attribute entirely: `open="false"` opens the item, because the value of a boolean attribute is not read.

Every switch is reported by the `toggle` event. It arrives after the state has changed, and if the state changed several times before dispatch, the events are merged into one. A plain HTML accordion does not need the event; it is useful when our code has to react to an item opening, for example to load data.

The example below shows two sets of three items. The set titled «Без name» (without name) consists of ordinary `details`, and the set titled «С name» (with name) gets a group, which the next section covers. Let us open all three items in the first set: they stay open at the same time. If the sets stand one under the other on a narrow screen, the behavior does not change. The result is easy to check: we open the second item in the set «С name», and the first one closes by itself. The live examples are shared with the Russian version of the article, so the text inside them is in Russian.

[Live example: Аккордеон на details: без name и с name](https://penlab.space/pen/eEQVCtd0)

## How to keep only one item open

For an accordion with one open item we give every `details` of the group the same `name`.

```html
<details name="faq" open>
  <summary>What is an accordion?</summary>
  <p>A list of items, each with a heading and hidden text.</p>
</details>

<details name="faq">
  <summary>Do we need JavaScript?</summary>
  <p>No, the browser opens the items itself.</p>
</details>

<details name="faq">
  <summary>Can two items be open at once?</summary>
  <p>No, opening an item closes the previous one.</p>
</details>
```

The browser keeps track of the group itself. In the set «С name» of the previous example we opened the items one after another, and in Chrome 154 only the last one stayed open. If we then click an item that is already open, it closes, and the group has no open items: `name` limits the number of open items from above, but does not require that one of them is always open.

A few details about the group:

- The items of a group do not have to follow each other in the markup: other code may stand between them. The group is defined by the `name` value, not by adjacency.
- A page can hold several accordions if each has its own `name`.
- If several items of a group have `open`, according to MDN only the first one in markup order will open. In Chrome 154 we checked a markup where the first item is closed and the second and third have `open`: the second one stayed open.
- When we open an item from code, the same rule applies: `details.open = true` closes the other items of the group, and each changed item gets its own `toggle` event.

In a browser without `name` support the attribute is ignored. The items stay independent, and each still opens and closes. Support for `name`: Chrome and Edge 120, Firefox 130, Safari 17.2.

## How to replace the arrow and style the heading

The browser draws a triangle before `summary`, the disclosure marker. By default `summary` behaves like a list item with `display: list-item`, so the marker is controlled the way a list marker is: `list-style: none` removes it, and `::marker` changes its look. According to MDN, the triangle also disappears when `display` is changed to `block`. Safari additionally shows the marker through the non-standard `::-webkit-details-marker`, and for it we add `display: none` to be safe. We draw our own arrow with the `::after` pseudo-element.

```html
<section class="faq">
  <details class="faq__item" name="faq" open>
    <summary class="faq__question">What is an accordion?</summary>
    <p class="faq__answer">A list of items: each has a heading, and the text under it is hidden until the item is open.</p>
  </details>
  <!-- the other items are the same -->
</section>
```

```css
.faq__question {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 14px 0;
  cursor: pointer;
  list-style: none;
}

.faq__question::-webkit-details-marker {
  display: none;
}

.faq__question::after {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.2s;
}

.faq__item[open] > .faq__question::after {
  transform: rotate(-135deg);
}

.faq__question:focus-visible {
  outline: 2px solid #6aa9ff;
  outline-offset: 2px;
}
```

The arrow turns by the `[open]` selector: the browser sets this attribute itself, so the item state and the arrow never drift apart. We do not remove the focus ring: without it a person who moves around the page with the Tab key cannot see which item they are on.

The item works from the keyboard without extra code. In Chrome 154 we moved to the heading with Tab, Enter opened the item, Space closed it, and a second Space opened it again.

[Live example: Аккордеон на details со своей стрелкой](https://penlab.space/pen/4F8596qm)

Inside `summary` it is better to keep only text and simple markup, without headings. According to MDN, in some browsers `summary` gets the button role, and a nested `h3` loses its heading role: a screen reader will not announce it as a heading. If a heading is needed in the page structure, the role has to be checked in the browsers and screen readers that matter.

## What to keep in mind: page search and limits

`details` has one more property: text inside a closed item can be found with the page search. According to the MDN compatibility tables, the search opens the item with the found text in Chrome and Edge from version 97, in Firefox from version 139 and in Safari from version 26.2. In Firefox before version 148 and in Safari 26.2 there is a bug: the page may not scroll to the found place.

There are limits too:

- On its own `details` does not animate the item: the switch is instant. Smoothness is added by the CSS in the next section.
- If an item needs complex behavior, for example loading from a server or closing on a condition, we need a script on the `toggle` event.
- To expand or collapse all items with one button we need a script with the `details.open` property. Items with a shared `name` will not open together this way: one stays open.

## How to open and close an item smoothly

The browser keeps the content of `details` in a separate part, and CSS got the `::details-content` pseudo-element for it. While the item is closed, this part has `content-visibility: hidden`, and on opening it changes to `visible`. In Chrome 154 we read this value on a closed and an open item through `getComputedStyle`: `hidden` and `visible`. Without a transition the content disappears instantly on closing, so smoothness rests on three techniques:

- `block-size: 0` and `overflow: clip` on the closed item and `block-size: auto` on the open one set where the height changes from and to;
- `interpolate-size: allow-keywords` allows animating the height between a number and the keyword `auto`, as in the article [“Smooth appearance of a block in CSS: how to animate display: none”](https://penlab.space/blog/css-transition-display-none);
- `content-visibility` in the transition list with the `allow-discrete` keyword keeps the content visible while the closing runs.

```css
.faq__answer {
  margin: 0;
  padding: 0 0 14px;
}

@supports (interpolate-size: allow-keywords) {
  :root {
    interpolate-size: allow-keywords;
  }

  .faq__item::details-content {
    block-size: 0;
    overflow: clip;
    transition: block-size 0.25s ease, content-visibility 0.25s allow-discrete;
  }

  .faq__item[open]::details-content {
    block-size: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .faq__question::after,
  .faq__item::details-content {
    transition: none;
  }
}
```

The `.faq__answer` rule with `margin: 0` is needed here: a paragraph has outer margins by default, and if they stay, the item jumps at the end of the transition. In Chrome 154 the item height in the last frames changed from 116.5 to 133 pixels, and with `margin: 0` it grew smoothly up to 115. We set the bottom spacing through `padding`. The pen adds page styles and borders between items to this code; they do not affect the behavior.

We measured the height of `::details-content` in Chrome 154 for an item with a paragraph of 62 pixels. On opening it grew from 0 to 62 pixels with intermediate values of 18, 48 and 57 pixels, and on closing it returned to 0, and the content stayed visible until the end of the transition.

We put the fallback into the `@supports` condition. Support differs between the parts: `::details-content` is understood by Chrome and Edge 131, Firefox 143 and Safari 18.4, while `interpolate-size` so far only by Chrome and Edge 129. In Firefox and Safari the condition is false, the block of rules is not applied, and the item works as an ordinary `details`: it opens and closes at once. We simulated this case in Chrome by replacing the condition with an obviously false one: the item opened and closed at once, without intermediate height values. We did not run the example in Firefox and Safari themselves and rely on the compatibility tables. For readers who set "reduce motion", the `prefers-reduced-motion` block turns the transitions off.

Below is the final accordion: a group through `name`, a custom arrow, smooth opening and a fallback. To see the animation better, we open the example in the Penlab editor and put `1s` instead of `0.25s` in both `transition` values of `.faq__item::details-content`.

[Live example: Аккордеон на details: группа и плавное раскрытие](https://penlab.space/pen/HqZjIZvu)

The easiest way to build your own accordion is a [new pen](https://penlab.space/new): it is free, and a finished example can be sent as a link. A related native element for windows above the page is covered in the article [“How to build a modal window with HTML, CSS and JS: the dialog element”](https://penlab.space/blog/modal-dialog-html-css).

## FAQ

### Do we need JavaScript for an accordion?

No. `details` opens and closes by itself, the `name` attribute makes an accordion with one open item, and CSS provides the smoothness. A script is needed when code has to react to an item opening: for that there is the `toggle` event.

### How to open an item by default?

We set the `open` attribute: `<details open>`. If several items of a group with one `name` are open, according to MDN the first one in markup order will open.

### Can all items of a group be closed?

Yes. A group with `name` allows no more than one open item, but does not require that one is open. In Chrome 154 a click on the open item closed it, and the group had no open items.

### How to open an item from JavaScript?

We write `details.open = true`. In a group with `name` the other items close. We checked it in Chrome 154: the item opened, the previous one closed, and each got a `toggle` event.

### What happens in an old browser?

The `name` attribute without support is ignored: the items work separately. The block with `::details-content` in our code sits under `@supports`, so without `interpolate-size` support the item opens without animation.

### Why is opening smooth but closing instant?

It is worth checking the transition list: it must include `content-visibility` with `allow-discrete`. According to MDN, without such a transition the content disappears instantly on closing.

## Sources

- [MDN: details element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details)
- [MDN: summary element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/summary)
- [MDN: ::details-content](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::details-content)
- [MDN: HTML details exclusive accordions](https://developer.mozilla.org/en-US/blog/html-details-exclusive-accordions/)
- [Chrome for Developers: Exclusive Accordion](https://developer.chrome.com/docs/css-ui/exclusive-accordion)
- [Chrome for Developers: More options for styling details](https://developer.chrome.com/blog/styling-details)
- [MDN: browser-compat-data compatibility tables](https://github.com/mdn/browser-compat-data)
