Modern HTML

Details & Summary

A cat looking at a computer screen

The <details> element creates a native disclosure widget. Its <summary> is the control; everything else inside <details> is the content it reveals.

Out of the box

No JavaScript required.
No ARIA required.
No classes required.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Details and summary</title>
</head>
<body>
  <h1>Frequently asked questions</h1>

  <details>
    <summary>Do I need JavaScript?</summary>
    <p>No. The browser manages the disclosure state and keyboard interaction.</p>
  </details>
</body>
</html>

Click the summary or focus it and press Enter or Space. The browser toggles the boolean open attribute on <details>.

Open by default

Add open when the content should begin expanded.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Open details</title>
</head>
<body>
  <details open>
    <summary>Shipping estimate</summary>
    <p>Most orders arrive in 3–5 business days.</p>
  </details>
</body>
</html>

open is a boolean attribute. Its presence means open, so open="false" is still open. Remove the attribute to close the disclosure.

A group of disclosures

Multiple <details> elements are independent by default. This is often better than an accordion because people can compare answers without repeatedly reopening them.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Disclosure group</title>
</head>
<body>
  <h1>Account help</h1>

  <details>
    <summary>Change my email address</summary>
    <p>Open Profile, choose Contact information, and enter your new address.</p>
  </details>

  <details>
    <summary>Reset my password</summary>
    <p>Use the “Forgot password?” link on the sign-in screen.</p>
  </details>

  <details>
    <summary>Delete my account</summary>
    <p>Contact support. We will confirm the request before removing data.</p>
  </details>
</body>
</html>

A native exclusive accordion

Give related <details> elements the same name and the browser will keep only one open at a time.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Native accordion</title>
</head>
<body>
  <h1>Course outline</h1>

  <details name="course" open>
    <summary>Chapter 1: HTML</summary>
    <p>Structure, semantics, forms, and native interaction.</p>
  </details>

  <details name="course">
    <summary>Chapter 2: CSS</summary>
    <p>Layout, responsive design, states, and animation.</p>
  </details>

  <details name="course">
    <summary>Chapter 3: JavaScript</summary>
    <p>Application behavior, data, and progressive enhancement.</p>
  </details>
</body>
</html>

Older browsers that do not understand name still get functional disclosures; they simply allow more than one to remain open.

Use an accordion on purpose

Hiding content adds interaction cost. Keep important, short content visible. Use an exclusive accordion only when opening one section at a time genuinely helps—not merely to make a page look shorter.

The toggle event

JavaScript is optional, but <details> exposes a toggle event when application logic needs to react to its state.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Details toggle event</title>
  <script src="/script.js" defer></script>
</head>
<body>
  <details id="delivery">
    <summary>Delivery details</summary>
    <p>Your package will require a signature.</p>
  </details>
  <p id="status" aria-live="polite">Delivery details are closed.</p>
</body>
</html>

The .open property reflects the open attribute and can also be changed directly:

const details = document.querySelector('details');
details.open = true;
details.open = false;

Styling the disclosure

Now that the native behavior is established, we can change its appearance. Keep the summary’s focus state and open/closed state obvious.

* { box-sizing: border-box; }

body {
  background: hsl(227 30% 17%);
  color: hsl(0 0% 96%);
  font: 1.125rem/1.6 system-ui, sans-serif;
  margin: 0;
  min-block-size: 100dvh;
  padding: 2rem;
}

main {
  inline-size: min(100%, 42rem);
  margin-inline: auto;
}

details {
  border-block-end: 1px solid hsl(227 15% 40%);
}

summary {
  cursor: pointer;
  font-size: 1.25rem;
  font-weight: 700;
  padding-block: 1rem;
}

summary::marker {
  color: hsl(47 100% 60%);
}

summary:hover {
  color: hsl(47 100% 70%);
}

summary:focus-visible {
  outline: 3px solid hsl(190 90% 55%);
  outline-offset: 4px;
}

.details__content {
  padding: 0 1.5rem 1rem;
}

.details__content > :last-child {
  margin-block-end: 0;
}

[ A11y Checklist ]

  • Put a <summary> first inside every <details>.
  • Write a summary that describes the hidden content; avoid vague labels such as “More.”
  • Do not add role="button", tabindex, or keyboard handlers. The browser already supplies the interaction.
  • Avoid links, buttons, or other interactive controls inside <summary>; competing actions make the control confusing.
  • Preserve a strong :focus-visible style if you replace browser styling.
  • Do not hide critical instructions, errors, or information a user must discover immediately.
  • Test zoom and long content: opening a disclosure should not make the next action difficult to find.