Skip to content
AgileLogics
7 min read

details, dialog, and popover

Prerequisites: ARIA roles, states, and properties

HTML now has built-in interactive components that used to need JavaScript libraries. They're accessible and keyboard-friendly by default.

details and summary

A disclosure widget — click the summary to show or hide the rest:

<details>
  <summary>Do you have gluten-free bread?</summary>
  <p>Yes — on Fridays and Saturdays, baked in a separate kitchen.</p>
</details>

<details open>
  <summary>What time do you open?</summary>
  <p>7:00 on weekdays, 8:00 on Saturdays.</p>
</details>
  • open starts it expanded; the browser toggles the attribute.
  • The browser's find in page (Ctrl + F) can open a closed details to show a match (Chrome and Edge since 2022; Firefox and Safari added it more recently).
  • Several details with the same name attribute form an accordion — opening one closes the others (Chrome/Edge 120, Firefox 130, Safari 17.2).
<details name="faq"><summary>Delivery</summary><p>Free over £20.</p></details>
<details name="faq"><summary>Returns</summary><p>Within 14 days.</p></details>

dialog

A modal dialog blocks the rest of the page until it closes:

<button type="button" id="open-dialog">Delete account…</button>

<dialog id="confirm" aria-labelledby="confirm-title">
  <h2 id="confirm-title">Delete your account?</h2>
  <p>This can't be undone.</p>
  <form method="dialog">
    <button value="cancel" autofocus>Cancel</button>
    <button value="delete">Delete</button>
  </form>
</dialog>

<script>
  const dialog = document.getElementById("confirm");
  document.getElementById("open-dialog").addEventListener("click", () => dialog.showModal());
  dialog.addEventListener("close", () => console.log("closed with:", dialog.returnValue));

  dialog.showModal();
  console.log("open:", dialog.open);
  dialog.querySelector('button[value="delete"]').click();
</script>
open: true
closed with: delete
  • showModal() opens it as a modal: the rest of the page becomes inert, focus moves into the dialog, Esc closes it, and it sits on the top layer above everything (style the backdrop with CSS ::backdrop).
  • form method="dialog" closes the dialog when submitted, and the pressed button's value becomes dialog.returnValue.
  • show() opens a non-modal dialog.
  • When it closes, focus returns to the element that opened it.

popover

The popover attribute makes any element a popover — menus, tooltips with interactive content, toasts — with no JavaScript:

<button type="button" popovertarget="share-menu">Share</button>
<div id="share-menu" popover>
  <ul>
    <li><a href="mailto:?body=https://example.com">Email</a></li>
    <li><button type="button" popovertarget="share-menu" popovertargetaction="hide">Close</button></li>
  </ul>
</div>
  • The button toggles the popover; the browser handles aria-expanded for you.
  • popover (same as popover="auto") closes when you click outside or press Esc ("light dismiss"), and opening one auto popover closes others. popover="manual" stays open until closed explicitly.
  • Popovers render on the top layer, but they're not modal: the rest of the page stays interactive. Use dialog + showModal() when the user must respond.
  • Supported in Chrome/Edge 114, Safari 17, and Firefox 125.

Positioning a popover next to its button is CSS's job — anchor positioning in CSS.

Practice

Build an FAQ of three questions as an accordion where only one answer can be open at a time.

Show solution
<details name="help" open>
  <summary>How do I order?</summary>
  <p>Online or in the shop.</p>
</details>
<details name="help">
  <summary>Can I pay by card?</summary>
  <p>Yes, all major cards.</p>
</details>
<details name="help">
  <summary>Do you deliver?</summary>
  <p>Within 5 km of the shop.</p>
</details>

Related developer tools