Skip to main content

Native Disclosure & Dialogs

advanced11 min readLesson 139 of 143

details/summary accordions with the name attribute, the dialog element with showModal, ::backdrop, autofocus placement, and method=dialog forms โ€” interactive UI without JavaScript.

For twenty years, "build a modal" meant JavaScript and an accessibility audit. The platform has absorbed that job. Disclosure widgets, accordions, and full modals are now native HTML โ€” and native beats a component library on every axis that matters: keyboard behavior, focus, semantics, and resilience when scripts fail.

Disclosure: details and summary

<details>
  <summary>What is your refund policy?</summary>
  <p>Full refund within 30 days, no questions asked.</p>
</details>

Zero JavaScript. The browser provides the toggle semantics, the disclosure state, Enter/Space activation on the summary, and screen-reader announcements. Details inside details nest naturally for multi-level content.

The accordion trap: five sibling <details> elements are not a coordinated accordion โ€” opening one leaves the others open. The name attribute โ€” shared between siblings โ€” makes the browser close the others for you:

<details name="faq">
  <summary>Can I change plan later?</summary>
  <p>Yes, prorated.</p>
</details>
<details name="faq">
  <summary>Do you offer invoices?</summary>
  <p>Every account, automatically.</p>
</details>

One attribute turns independent disclosures into an exclusive accordion. (Reminder from Beginner, now with advanced eyes: summary computes the details' accessible name โ€” never leave it empty.)

The dialog element

<dialog id="confirm">
  <h2 id="confirm-h">Delete this file?</h2>
  <p>This cannot be undone.</p>
  <button autofocus>Cancel</button>
  <button>Delete</button>
</dialog>
document.getElementById("confirm").showModal(); // modal
document.getElementById("confirm").show();      // non-modal

showModal() gives you the full modal package from the platform: the rest of the page becomes inert, Escape closes, focus is trapped inside, and it closes the browser's top layer โ€” above every z-index hack you have ever written. show() opens it in normal flow for non-modal use. Style the backdrop with the ::backdrop pseudo-element; the autofocus attribute inside the dialog decides which control receives focus when it opens โ€” put it on the safest action ("Cancel"), not the destructive one.

Name the dialog with aria-labelledby pointing at its heading, and close it from a form or code with dialog.close() โ€” or declaratively, with the invoker commands you will meet in the next lesson.

Forms inside dialogs

A <form method="dialog"> closes its dialog on submit and hands the return value to code via dialog.returnValue:

<dialog id="pick">
  <form method="dialog">
    <p>Pin this board?</p>
    <button value="pin">Pin it</button>
    <button value="cancel" autofocus>Cancel</button>
  </form>
</dialog>

This is the constraint-validation knowledge from Intermediate finding a new home: method="dialog" composes with native validation โ€” an invalid required field blocks submission, so the dialog stays open with the error the browser already renders.

Dialog vs popover vs details โ€” choosing

| Need | Reach for | | --- | --- | | Must block the page until answered | <dialog> + showModal() | | Transient layer, light-dismiss on outside click | popover (next lesson) | | In-flow progressive content | <details> |

Modal for decisions, popover for tools, details for content that belongs to the page's own flow. Reaching for a modal when the content is in-flow is the classic over-engineer tell.

What goes wrong

  • Hand-rolled modals: position: fixed + a z-index arms race + a focus trap you wrote yourself and now must maintain. <dialog> replaces all of it.
  • show() where showModal() was meant: no inert backdrop, no Escape, no focus containment โ€” "the dialog works but the page is still clickable behind it".
  • Focus lands on the destructive button because nobody set autofocus.
  • Accordion by class toggling: JS closes siblings that name would close natively.
  • Empty <summary> โ€” a disclosure that announces nothing.

Practice and where this connects

You will build a working FAQ accordion with name, then a confirmation dialog with backdrop, autofocus, and a method="dialog" form โ€” first declaratively, then by fixing a broken modal that skipped every one of these details. Native interactive elements are the backbone of the accessibility engineering section later: the correct pattern is usually the one the platform already ships.

Now practice

Native Disclosure & Dialogs โ€” PracticeBuild a native FAQ accordion, a confirmation dialog with backdrop and autofocus, then debug a hand-rolled modal.3 challenges ยท ยท ~25 min