The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Create a disclosure<details> <summary>More options</summary> <p>Details</p> </details>View examples
Open initially<details open> <summary>Status</summary> <p>Ready</p> </details>View examples
Group disclosures<details name="faq"> <summary>Question</summary> <p>Answer</p> </details>View examples
Declare a dialog<dialog id="settings"><p>Settings</p></dialog>View examples
Open modallydocument.querySelector('#settings').showModal()View examples
Open non-modallydocument.querySelector('#settings').show()View examples
Close with a resultdocument.querySelector('#settings').close('saved')View examples
Submit and close<form method="dialog"> <button value="cancel">Cancel</button> </form>View examples
Style the modal backdropdialog::backdrop { background: rgb(0 0 0 / 55%); }View examples
Declare an auto popover<div id="help" popover>Helpful text</div>View examples
Toggle declaratively<button popovertarget="help">Help</button>View examples
Choose an action<button popovertarget="help" popovertargetaction="show"> Show help </button>View examples
Keep a popover open<div id="status" popover="manual">Saved</div>View examples
Show with JavaScriptdocument.querySelector('#status').showPopover()View examples
Style an open popover[popover]:popover-open { opacity: 1; }View examples

Use the platform's interactive elements before recreating them with generic containers. Details provides a disclosure, dialog provides modal or non-modal windows, and popover provides lightweight top-layer UI. Give every control a clear name, preserve keyboard behavior, and use JavaScript only for state the declarative APIs cannot express.

Step by step

Detailed examples

01

Reveal optional content with details

The first summary child supplies the disclosure label and activation control. Content after it is revealed when open is present. A shared name creates an exclusive group, but each details element still needs the same name and should appear in a sensible document order.

An exclusive FAQ group
<details name="faq" open>
  <summary>Can I export my data?</summary>
  <p>Yes. Open Settings, then choose Export.</p>
</details>
<details name="faq">
  <summary>Can I delete my account?</summary>
  <p>Yes. The confirmation explains what will be removed.</p>
</details>

Note: Do not place another interactive control inside summary; the summary itself is the disclosure control.

Back to quick reference ↑
02

Choose modal or non-modal dialog behavior

A dialog has no implicit opening trigger. Call show() for a window that can coexist with the page, or showModal() when users must respond before continuing. Do not simulate opening by setting open directly for a modal: that skips top-layer and inert behavior.

Open a non-modal inspector
<button id="open-inspector">Open inspector</button>
<dialog id="inspector">
  <p>Page diagnostics</p>
  <button id="close-inspector">Close</button>
</dialog>
<script>
  const dialog = document.querySelector('#inspector');
  document.querySelector('#open-inspector').addEventListener('click', () => dialog.show());
  document.querySelector('#close-inspector').addEventListener('click', () => dialog.close());
</script>
Back to quick reference ↑
03

Complete modal interactions

showModal() gives modal dialogs top-layer placement and makes content outside them inert. Include an obvious close path. A form with method=dialog closes through submit buttons and records the activated button's value; inspect returnValue in the close event. Escape normally fires cancel and closes the dialog unless that event is deliberately prevented.

Confirm with native dialog semantics
<button id="remove">Remove item</button>
<dialog id="confirm-remove" aria-labelledby="confirm-title">
  <form method="dialog">
    <h2 id="confirm-title">Remove this item?</h2>
    <p>This action cannot be undone.</p>
    <button value="cancel">Cancel</button>
    <button value="remove">Remove</button>
  </form>
</dialog>
<script>
  const dialog = document.querySelector('#confirm-remove');
  document.querySelector('#remove').addEventListener('click', () => dialog.showModal());
  dialog.addEventListener('close', () => console.log(dialog.returnValue));
</script>
Back to quick reference ↑
04

Use popovers for lightweight top-layer UI

The popover attribute hides an element until a control or method opens it. The default auto state supports light-dismiss with Escape or an outside interaction and normally closes other auto popovers. popovertarget creates the relationship and exposes expanded state to accessibility APIs.

A declarative help popover
<button type="button" popovertarget="keyboard-help">Keyboard help</button>
<div id="keyboard-help" popover>
  <p><kbd>Ctrl</kbd> + <kbd>K</kbd> opens search.</p>
</div>
Back to quick reference ↑
05

Control popover lifetime explicitly

Use popovertargetaction when a control should only show or hide rather than toggle. Manual popovers do not light-dismiss, so the application must provide a reliable close path. Programmatic methods are useful for events without a natural invoking button, such as status messages.

Manual status with explicit controls
<button id="show-status">Show status</button>
<div id="status" popover="manual">
  <p>Upload complete.</p>
  <button popovertarget="status" popovertargetaction="hide">Dismiss</button>
</div>
<script>
  const status = document.querySelector('#status');
  document.querySelector('#show-status').addEventListener('click', () => status.showPopover());
</script>
Back to quick reference ↑
06

Style top-layer states without hiding semantics

Modal dialogs and displayed popovers participate in the top layer, outside ordinary stacking-context competition. Use ::backdrop for the layer behind a modal and :popover-open for displayed popovers. Preserve visible focus and sufficient contrast; animation must not make controls unavailable while state changes.

Dialog and popover state styles
dialog {
  border: 0;
  border-radius: 0.75rem;
  max-inline-size: min(32rem, 90vw);
}

dialog::backdrop {
  background: rgb(15 23 42 / 60%);
}

[popover] {
  border: 1px solid #94a3b8;
  border-radius: 0.5rem;
}

[popover]:popover-open {
  opacity: 1;
}
Back to quick reference ↑

Local code tester

Try a disclosure, dialog, and popover

Open each native interaction and inspect its keyboard, dismissal, and top-layer behavior.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. WHATWGHTML: The details and summary elementshtml.spec.whatwg.org
  2. WHATWGHTML: The dialog elementhtml.spec.whatwg.org
  3. WHATWGHTML: Popovershtml.spec.whatwg.org
  4. MDN Web DocsThe dialog elementdeveloper.mozilla.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback