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.
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::backdroppseudo-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 to1if not set, so if you’re using percentages, setmax="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 nearmaxwhen 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 nearminwhen 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. OnlyshowModal()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 to1. - Setting
optimumincorrectly (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
- Use the Practice in Editor button. Add a third
<details>FAQ item of your own. - Change the
<progress>value to100and observe the bar fill completely. - Change the
<meter>’soptimumfrom0to10and think about how that would change which end of the gauge is shown as “good,” even though the underlyingvaluehasn’t changed. - 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>withshowModal()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 (valueofmax); leaving outvalueshows an indeterminate loading state.<meter>shows a measurement within a range, andoptimumtells the browser which direction is actually “good” — the key difference from<progress>.