HTMLIntermediate

HTML details, dialog, progress and meter Elements

Learn HTML's native interactive elements: details/summary for accordions and FAQs, dialog for modals, progress for task completion, and meter for gauges.

All HTML lessons

What you will learn

HTML has native elements for four extremely common UI patterns — a collapsible section, a modal popup, a progress bar, and a measurement gauge — that developers frequently rebuild from scratch with <div>s and JavaScript, unaware the browser already does most of the work. In this lesson you will learn <details>/<summary>, <dialog>, <progress>, and <meter>, when each genuinely fits, and where their native behaviour actually beats a custom-built version.

<details> and <summary>: a native accordion

<details>
  <summary>What payment methods do you accept?</summary>
  <p>We accept credit cards, UPI, and net banking.</p>
</details>

<details> creates a collapsible section. <summary> is the always-visible clickable heading — clicking it toggles the rest of the content inside <details> open or closed. Try it in the practice editor: no CSS, no JavaScript, and it already works — keyboard-accessible, click-to-toggle, with a little triangle/arrow indicator that rotates automatically.

open: starting expanded

<details open>
  <summary>This one starts open</summary>
  <p>Visible immediately, no click needed.</p>
</details>

The boolean open attribute makes the section start already expanded, rather than the default collapsed state.

Real use case: an FAQ page

<h2>Frequently Asked Questions</h2>

<details>
  <summary>How long does shipping take?</summary>
  <p>Standard shipping takes 5-7 business days.</p>
</details>

<details>
  <summary>Can I change my order after placing it?</summary>
  <p>Yes, within 1 hour of placing the order, from your account page.</p>
</details>

<details>
  <summary>Do you ship internationally?</summary>
  <p>Currently we only ship within India.</p>
</details>

This is genuinely the ideal use case: this exact pattern (recall the custom-built ARIA accordion from the previous lesson) requires zero JavaScript, zero ARIA attributes, and gets keyboard support automatically. Reach for <details>/<summary> first, every time a plain expand/collapse is all you need — only build a custom version with <div> and ARIA when you need behaviour this native element genuinely can’t provide (like only ever allowing one section open at a time).

Styling the marker

The little default triangle/arrow can be restyled or removed with CSS, if you want a custom icon instead:

summary {
  cursor: pointer;
  font-weight: bold;
}

summary::marker {
  color: tomato;
}

summary::marker is a CSS pseudo-element that specifically targets that triangle — you’ll cover pseudo-elements properly in the CSS course.

<dialog>: a native modal popup

<dialog id="confirm-dialog">
  <p>Are you sure you want to delete this item?</p>
  <button id="confirm-yes">Yes, delete it</button>
  <button id="confirm-no">Cancel</button>
</dialog>

<button id="delete-trigger">Delete item</button>
const dialog = document.getElementById("confirm-dialog");
document.getElementById("delete-trigger").addEventListener("click", () => {
  dialog.showModal();
});
document.getElementById("confirm-no").addEventListener("click", () => {
  dialog.close();
});

<dialog> creates a genuine modal window — a popup that sits on top of the rest of the page. Unlike <details>, it needs a small amount of JavaScript to open (you’ll cover this fully once you reach the JavaScript DOM lessons — the snippet above is just a preview so you recognise the pattern). What makes it worth knowing about even now:

  • dialog.showModal() opens it as a true modal: it visually sits above everything else, the browser dims the rest of the page automatically (styleable via the ::backdrop pseudo-element), and — crucially — it traps keyboard focus inside itself, so Tab cycles only through the dialog’s own contents rather than leaking out to the page behind it.
  • Pressing the Escape key closes it automatically — you get this entirely for free, with zero extra code.
  • dialog.close() closes it from your own button’s click handler.
  • There’s also a plain dialog.show() (no “Modal”), which opens it as a non-modal panel — visible, but not blocking interaction with the rest of the page, and without the automatic focus-trapping or Escape-to-close behaviour.

Why this matters compared to a hand-built “modal”

Before <dialog> existed, developers built modals entirely from a <div> styled to look like a popup — and correctly trapping keyboard focus inside a hand-built modal (so Tab doesn’t silently escape behind it) is a notoriously easy thing to get subtly wrong. <dialog> gives you correct focus-trapping and Escape-to-close behaviour for free, which is real, meaningful accessibility work you’d otherwise have to build and test yourself.

Styling the backdrop

dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

dialog {
  border: none;
  border-radius: 8px;
  padding: 24px;
}

::backdrop styles the dimmed overlay behind the dialog, shown automatically the moment showModal() runs.

<progress>: showing task completion

<progress value="70" max="100"></progress>

A progress bar for something with a known, tracked amount of completion — a file upload, a multi-step form, a download.

  • value — how much is done so far.
  • max — the total amount when fully done (defaults to 1 if not set, so if you’re using percentages, set max="100" explicitly).

Indeterminate progress

<progress></progress>

Leaving out value entirely (with or without max) shows an indeterminate state — a bar that animates without showing a specific percentage, exactly right for “loading, but we don’t know how long it’ll take” situations, like waiting on a network response of unknown duration.

Real use case

<label for="upload-progress">Uploading resume.pdf...</label>
<progress id="upload-progress" value="45" max="100"></progress>

A file upload widget updating value in real time via JavaScript as bytes actually transfer — again, something you’ll wire up fully once you reach the JavaScript DOM lessons.

<meter>: showing a measurement within a range

<meter value="8" min="0" max="10" low="3" high="8" optimum="0"></meter>

This one is easy to confuse with <progress>, because they can look almost identical — but they mean genuinely different things.

The real distinction: <progress> is for “how much of a task is done.” <meter> is for “where does this value fall within a known range,” like a fuel gauge, a disk-usage indicator, or a test score — the number itself is the point, not a completion percentage toward finishing something.

<!-- Battery level -->
<label for="battery">Battery</label>
<meter id="battery" value="0.72" min="0" max="1"></meter> 72%

<!-- Disk usage: high values are "bad" here -->
<label for="disk">Disk usage</label>
<meter id="disk" value="85" min="0" max="100" low="50" high="80" optimum="0"></meter>

<!-- Test score: high values are "good" here -->
<label for="score">Test score</label>
<meter id="score" value="92" min="0" max="100" low="50" high="80" optimum="100"></meter>
  • min / max — the range’s boundaries.
  • low / high — thresholds dividing the range into low/medium/high segments.
  • optimum — tells the browser which end is actually “good.” Set it near max when higher values are better (a battery level, a test score) — the browser may render the gauge in a reassuring colour (often green) when the value is in the optimum zone. Set it near min when lower values are better (disk usage, error count) — now a high value shifts toward a warning colour instead, even though the raw number itself is high in both cases. This is the detail most tutorials skip, and it’s what makes <meter> genuinely smarter than a plain progress bar for this kind of data.

Putting it together: a real page

<h2>Frequently Asked Questions</h2>

<details>
  <summary>What is your return policy?</summary>
  <p>Items can be returned within 30 days, unused and in original packaging.</p>
</details>

<h2>Your Storage</h2>
<label for="storage-meter">8 GB of 10 GB used</label>
<meter id="storage-meter" value="8" min="0" max="10" low="6" high="9" optimum="0"></meter>

<h2>Uploading Backup...</h2>
<progress value="55" max="100"></progress>

<dialog id="confirm-dialog">
  <p>Delete this backup permanently?</p>
  <button>Yes, delete</button>
  <button>Cancel</button>
</dialog>

Common mistakes

  • Building a custom accordion from <div>s and JavaScript when <details>/<summary> already does the job natively, for free, with better accessibility out of the box.
  • Using dialog.show() when you actually need a true modal. Only showModal() traps focus and enables Escape-to-close.
  • Building a hand-rolled modal <div> instead of <dialog>, and then not correctly trapping keyboard focus inside it — a very easy accessibility bug to introduce, and one <dialog> avoids entirely.
  • Confusing <progress> and <meter>. Task completion → <progress>. A measurement within a range (where you also care which direction is “good”) → <meter>.
  • Forgetting max="100" on <progress> when working with percentages, since it silently defaults to 1.
  • Setting optimum incorrectly (or leaving it out) on a <meter> where direction genuinely matters, like disk usage — losing the colour-coding benefit that makes <meter> worth using over <progress> in the first place.

Interview-style questions

What does <details>/<summary> give you for free, compared to a hand-built accordion? Click-to-toggle behaviour, keyboard accessibility, and a toggle indicator — all without any JavaScript or ARIA attributes.

What’s the key accessibility advantage of <dialog>’s showModal() over a custom <div>-based modal? It automatically traps keyboard focus inside the dialog and closes on Escape, both of which are easy to get wrong when building a modal from scratch.

What is the actual difference in meaning between <progress> and <meter>? <progress> represents how much of a task is complete. <meter> represents a measurement’s position within a known range, and can indicate via optimum which direction (higher or lower) is actually desirable.

What happens if you use <progress> with no value attribute? It renders in an indeterminate state — an animated bar with no specific percentage shown, suited to situations where completion can’t yet be measured.

Practice

  1. Use the Practice in Editor button. Add a third <details> FAQ item of your own.
  2. Change the <progress> value to 100 and observe the bar fill completely.
  3. Change the <meter>’s optimum from 0 to 10 and think about how that would change which end of the gauge is shown as “good,” even though the underlying value hasn’t changed.
  4. Explain in your own words when you’d reach for <meter> instead of <progress> for a battery-level indicator.

Recap

  • <details>/<summary> creates a native, keyboard-accessible collapsible section with zero JavaScript — the right first choice for FAQs and simple accordions.
  • <dialog> with showModal() creates a true modal: it traps keyboard focus and closes on Escape automatically, avoiding common hand-built modal accessibility bugs.
  • <progress> shows completion of a task (value of max); leaving out value shows an indeterminate loading state.
  • <meter> shows a measurement within a range, and optimum tells the browser which direction is actually “good” — the key difference from <progress>.