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>
openstarts it expanded; the browser toggles the attribute.- The browser's find in page (Ctrl + F) can open a closed
detailsto show a match (Chrome and Edge since 2022; Firefox and Safari added it more recently). - Several
detailswith the samenameattribute 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'svaluebecomesdialog.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-expandedfor you. popover(same aspopover="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>