RizTech Academy logo
RizTech Academy
HTML FoundationsLesson 9 of 925 min

details, summary and dialog: interactive with no JavaScript

An accordion and a modal dialog are the two components every site eventually needs, and the usual route to both is a JavaScript tutorial that produces something keyboard users cannot operate. HTML has them built in. They are accessible by default, they work before your JavaScript loads, and they work if your JavaScript never loads at all.

<details> and <summary>

<details>
  <summary>Do you deliver to Karve Nagar?</summary>
  <p>Yes, ₹20 for orders under ₹500 and free above it.</p>
</details>

That is a working disclosure widget. Click the summary and it opens; click again and it closes. No CSS, no JavaScript.

What you get for free, and each of these is something hand-rolled accordions usually get wrong:

  • Keyboard operation. Tab to it, Enter or Space to toggle.
  • The right announcement. A screen reader says "Do you deliver to Karve Nagar, collapsed, button" — and "expanded" after.
  • Browser find-in-page opens it. Ctrl + F for text inside a closed <details> finds it and opens the section. A display: none accordion hides its content from search entirely.
  • It prints open in some browsers, and can be forced to.

Rules: <summary> must be the first child, and there is exactly one. Everything after it is the content. Add open to start expanded:

<details open>
  <summary>Opening hours</summary>
  <p>7am to 10pm, every day.</p>
</details>

An accordion

<details name="faq">
  <summary>Do you deliver?</summary>
  <p>Yes, across Kothrud and Karve Nagar.</p>
</details>

<details name="faq">
  <summary>What are your hours?</summary>
  <p>7am to 10pm.</p>
</details>

Giving several <details> the same name makes them exclusive — opening one closes the others, which is what an accordion is. This is recent and genuinely replaced a JavaScript component. Check caniuse.com before relying on it; where it is unsupported, all the panels simply open independently, which is a perfectly acceptable fallback.

Styling it

Two things beginners hit immediately.

Removing the triangle:

summary {
  list-style: none;        /* Firefox, and the standard way */
  cursor: pointer;
}
summary::-webkit-details-marker {
  display: none;           /* older Safari and Chrome */
}

Styling the open state, using a selector from module 6:

details[open] > summary {
  font-weight: 600;
}

One real limitation: you cannot animate the open/close transition with CSS alone in every browser yet, because the content goes from display: none to displayed. Newer CSS is fixing this; for now, accept the instant toggle. An instant accordion that works everywhere beats an animated one that traps keyboard users.

And <summary> is already a button as far as assistive technology is concerned. Do not put a <button> inside it — you get a button inside a button, and the keyboard behaviour becomes unpredictable.

<dialog>

A modal dialog is where hand-written components fail worst. The hard parts are focus trapping, returning focus afterwards, Escape to close, and making the rest of the page inert. <dialog> does all four.

<dialog id="enquiry">
  <form method="dialog">
    <h2>Send an enquiry</h2>
    <label for="phone">Your phone number</label>
    <input type="tel" id="phone" name="phone" required>
    <button type="submit">Send</button>
    <button type="submit" value="cancel">Cancel</button>
  </form>
</dialog>

This element does need two lines of JavaScript to open, which is the one exception in this lesson:

<button type="button" onclick="document.getElementById('enquiry').showModal()">
  Send an enquiry
</button>

For that price you get:

  • Focus moves into the dialog and is trapped there, so Tab cannot wander into the page behind it.
  • Escape closes it, which every user expects and most custom modals ignore.
  • The rest of the page becomes inert — not clickable, and skipped by screen readers.
  • Focus returns to the button that opened it when it closes. Losing this is why custom modals dump a keyboard user back at the top of the page.
  • A ::backdrop pseudo-element for the dim overlay:
dialog::backdrop {
  background: rgb(0 0 0 / 0.5);
}

<form method="dialog"> is the trick worth knowing: a submit button inside it closes the dialog without submitting anything anywhere, and the button's value tells you which one was pressed. That is how you get a Cancel button with no JavaScript.

showModal() versus show()

showModal() gives you everything above. show() opens it as a non-modal panel — no backdrop, no focus trap, no inertness, and the page behind stays usable. You almost always want showModal(); show() is for a toast or a picker.

Setting the open attribute directly in HTML also opens it non-modally, and it is usually a mistake for the same reason.

Do not build a modal out of a div

<!-- the usual tutorial version -->
<div class="modal" style="display:none">…</div>

Focus stays in the page behind it, Escape does nothing, a screen reader reads the hidden page as if it were still there, and a keyboard user can tab to buttons they cannot see. Getting all of that right by hand takes about a hundred lines and an understanding of inert and aria-modal. The element does it.

The other native ones worth knowing

<datalist> — a text field with suggestions, where a value not in the list is still allowed:

<label for="area">Area</label>
<input type="text" id="area" name="area" list="areas" autocomplete="off">
<datalist id="areas">
  <option value="Kothrud">
  <option value="Karve Nagar">
  <option value="Warje">
</datalist>

That is the difference from <select>: a <select> restricts to the options, a <datalist> suggests them. Right for a city or area field where you cannot list everywhere.

<progress> and <meter> — a task's progress, and a measurement in a range:

<progress value="3" max="5">3 of 5</progress>
<meter value="7" min="0" max="10" low="3" high="8">7 out of 10</meter>

Both announce their value to a screen reader. A <div> with a coloured width does not.

<output> — for a calculated result, which a screen reader announces when it changes.

The principle

Reach for the native element first. You get keyboard support, screen reader announcements, focus management, find-in-page and printing, all maintained by the browser vendors, at no cost in bytes.

You give up some styling control — that is the honest trade, and it is why design teams often reject <details> for an accordion that must animate. But the default should be the native element, with a deliberate reason for leaving it, not the other way round.

This is also the last time this course asks you to write JavaScript. Everything from here is CSS.

Check your work

What <details> gives you free. Keyboard toggling, correct announcements, find-in-page opening it, and printing.

Where <summary> must go. First child, exactly one.

How to make an exclusive accordion. Share a name across several <details>.

Why not a <button> inside <summary>. <summary> is already a button.

The <details> limitation. No reliable CSS animation of open and close yet.

Four things <dialog> handles. Focus trapping, Escape, page inertness, and returning focus.

showModal() versus show(). Modal with backdrop and focus trap, versus a plain panel.

What <form method="dialog"> does. Closes the dialog on submit without sending anything; value says which button.

Why a div modal fails. Focus escapes, Escape does nothing, and the hidden page is still read aloud.

<datalist> versus <select>. Suggests versus restricts.

Why <progress> beats a coloured div. It announces its value.

Practice

  1. Build a three-question FAQ with <details> and <summary>. Operate it with the keyboard only.
  2. Listen to one with a screen reader. Note the collapsed and expanded announcements.
  3. Close a <details>, then Ctrl + F for text inside it. Confirm it opens.
  4. Add name="faq" to all three and confirm only one opens at a time. Check the support on caniuse.com.
  5. Remove the triangle and style details[open] > summary.
  6. Put a <button> inside a <summary> and try to use it with the keyboard.
  7. Build a <dialog> with showModal(). Tab repeatedly and confirm focus cannot leave it.
  8. Press Escape. Then check where focus went after it closed.
  9. Style dialog::backdrop.
  10. Add a Cancel button with <form method="dialog"> and no JavaScript.
  11. Switch to show() and note everything you lose.
  12. Build the same modal from a <div> and compare the two on keyboard use alone.
  13. Add a <datalist> for areas, then type a value that is not in it.
  14. Add a <progress> and listen to it with a screen reader.

Official documentation

Next module: CSS — starting with why your unstyled page already has styles.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship