learn-html-with-phoebe / Deep dive 6.1 of 3
Learn HTML with Phoebe · Deep dive 6.1 of 3

Interactive without JavaScript: HTML's hidden superpowers

A decade ago, an accordion meant a jQuery plugin, a modal meant a library, and a tooltip meant a fight with z-index. Today all three are attributes. This dive tours the interactive side of pure HTML: details, dialog, popover, and the keyboard focus system that makes them usable by everyone.

🟠 Self-paced ~30 min
details / summary dialog popover focus + tabindex
Part 0

Why this deep dive exists

This course has quietly held a line: HTML says what things ARE, and behavior belongs elsewhere. Here is the plot twist - the "elsewhere" has been shrinking. Browsers absorbed the most common interactive patterns into HTML itself, so a growing family of clicks, toggles, and overlays now ships with zero scripts. Every example below runs live in this page's script-free preview panels, which is itself the proof.

Core - read carefully Self-study - recognition level ★ Try it now Orange panels are editable - type and watch
★ What you walk out with A working FAQ accordion for your project page (details), an in-page dialog you understand honestly (including which half still needs JavaScript), tooltips and menus via the popover attribute, and the keyboard rules - tab order, tabindex, visible focus - that decide whether real people can actually use any of it.
Then: a library Now: an attribute Accordion plugin script + CSS + init call <details name="faq"> exclusive open built in Modal library overlay, focus trap, Esc key <dialog> one JS line for modal mode Tooltip / dropdown library positioning + z-index wars popover + popovertarget top layer, light dismiss, free Hand-rolled focus code onclick on divs, broken Tab real buttons + tabindex source order = tab order Fewer scripts is not just less code: built-in behavior is accessible, consistent, and future-proof by default.
🔍 Click to zoom - four patterns that each cost a JavaScript library in 2014, now shipping inside HTML itself.
Part 1 · covers web.dev "Details and summary"

details and summary - the disclosure widget core

A disclosure widget is a click-to-reveal: question closed, answer hidden, click, answer shown. You have used it on this very page all course long - every collapsible card here is a <details> element wearing a class="card" outfit. Underneath the styling, the open-close behavior is 100% free HTML.

LiveOne element, zero scripts4 min▶

The recipe: a <details> element whose first child is a <summary> (the always-visible clickable line). Everything after the summary is hidden until the user opens it. Add the open attribute to start expanded. The browser supplies the click handling, the keyboard handling, and the screen-reader announcement ("collapsed" / "expanded") - all of it.

One element, zero scripts: details plus summary THE MARKUP THE RENDERED WIDGETS <details open> <summary>Does this need JS?</summary> <p>No. The browser handles it.</p> </details> <details><summary> Can it start open?</summary></details> ▾ Does this need JS? No. The browser handles it. ▸ Can it start open? The open attribute is the only difference between these two - same element, same zero scripts.
🔍 Click to zoom - the triangle and the reveal are free, the browser supplies both
<h3>FAQ</h3>
<details open>
  <summary>Does this need JavaScript?</summary>
  <p>No. The browser handles the click,
  the keyboard, and the screen reader.</p>
</details>
<details>
  <summary>Can it start open?</summary>
  <p>Yes - put the open attribute on it,
  like the first question here.</p>
</details>

One distinction worth making: the playground above uses plain, unstyled details - that is the element as taught. The cards on this course page are the same element plus CSS. Same skeleton, different clothes: exactly the HTML-vs-CSS split this course keeps preaching.

Real world

GitHub's "show more" sections, MDN's collapsible sidebars, and thousands of documentation FAQs are bare details elements. View source on a GitHub release changelog and you will find them - now you can read exactly what they are.

LiveExclusive accordions with the name attribute3 min▶

Give several details elements the same name value and they become an exclusive accordion: opening one closes the others, like radio buttons for content. This is a recent addition - supported in Chrome and Edge 120+, Safari 17.2+, and Firefox 130+ (so roughly every browser updated since 2024). Older browsers simply ignore name and allow multiple panels open at once - a graceful downgrade, not a breakage.

<details name="faq">
  <summary>Q1 · What closes me?</summary>
  <p>Opening any other panel that
  shares my name attribute.</p>
</details>
<details name="faq">
  <summary>Q2 · Open me to test it</summary>
  <p>Q1 just snapped shut. Radio-button
  logic, zero scripts.</p>
</details>
<details name="faq">
  <summary>Q3 · Me too</summary>
  <p>One name group, one open panel.</p>
</details>
Self-studyGotchas: summary rules and when NOT to hide content2 min read▶
  • summary must be the first child of details. Put it later (or omit it) and the browser renders a default "Details" label - a sure sign of a markup slip.
  • The triangle marker is styleable, not sacred. It is the ::marker pseudo-element in CSS; this page's cards replace it with the ▶ you see. HTML stays identical.
  • Chrome can open them for you: find-in-page (Ctrl/Cmd+F) auto-expands a closed details if the match is inside. Handy, but do not rely on it cross-browser.
  • Do not bury critical content. Hiding "how to cancel your subscription" three accordions deep is a dark pattern. Disclosure is for depth-on-demand, not for making things hard to find.
Part 2 · covers web.dev "Dialog"

dialog - the conversation box core

A dialog is a box that interrupts the page to talk to the user: confirmations, warnings, forms-in-a-box. The <dialog> element gives you the box, the semantics, and (in modal mode) the dimmed backdrop and focus handling that libraries used to charge kilobytes for.

LiveThe open attribute - a dialog living in the page3 min▶

A <dialog> without the open attribute exists in your markup but renders nothing - it waits. Add open and it appears in the flow of the page as a non-modal box: visible, styled by the browser with a strong border, but not blocking anything around it.

<dialog open>
  <p><strong>Heads up!</strong>
  I am a dialog with the open attribute.</p>
  <p>The page around me still works -
  that is what non-modal means.</p>
</dialog>
<p>Regular page content, still clickable.</p>
LiveModal mode - the honest JavaScript half3 min▶

The famous dialog experience - dimmed page, centered box, Esc to close, focus trapped inside - is modal mode, and honesty requires saying it plainly: opening a modal takes one line of JavaScript. The element does all the hard parts; the script is just the doorbell.

The whole pattern<button id="ask">Delete account</button>
<dialog id="confirm">
  <p>Really delete everything?</p>
  <form method="dialog"><button>Cancel</button></form>
</dialog>
<script>
  ask.onclick = () => confirm.showModal();
</script>

What that one showModal() call buys, free: the page behind gets dimmed (styleable via ::backdrop), keyboard focus is trapped inside the box, Esc closes it, and everything behind becomes inert - unclickable and invisible to screen readers. Note the second HTML gem in there: a <form method="dialog"> closes its dialog on submit, no script for the closing side.

Preview limitation, stated honestly This page's playground iframes run with scripts disabled, so showModal() cannot be demoed live here. Paste the pattern above into your own index.html and open it in a real tab - the full modal experience appears.
Self-studyDialog manners2 min read▶
  • Modals are interruptions - spend them carefully. Destructive confirmations and must-answer questions earn one; "subscribe to our newsletter" on arrival does not.
  • Always provide a visible close button. Esc works in modal mode, but not everyone knows that, and touch users have no Esc.
  • Do not fake dialogs with divs. A div overlay gets none of the free behavior: no focus trap, no Esc, no inert background, no announcement. This is the "real button vs onclick div" lesson at box scale.
Part 3 · covers the popover attribute (MDN Popover API) · support flagged

popover - tooltips and menus with zero scripts core

The newest superpower of the three. Put the popover attribute on any element and it becomes a hidden overlay; point a button at it with popovertarget and the button toggles it. Tooltips, dropdown menus, hint bubbles - the pattern that launched a thousand libraries is now two attributes. Supported in all modern browsers since 2024 (Chrome 114+, Safari 17+, Firefox 125+); older browsers show the button doing nothing, so keep essential content out of popovers for a while yet.

Livepopover + popovertarget - the whole API4 min▶

Two halves: the overlay element carries popover plus an id; the trigger button carries popovertarget pointing at that id. Click toggles it open and closed. Click anywhere else and it dismisses itself ("light dismiss") - the behavior every hand-rolled dropdown gets wrong on the first try.

popovertarget on the button, id on the popover - one wire THE MARKUP THE RENDERED OVERLAY <button popovertarget="hint"> What is a disclosure widget? </button> <div id="hint" popover> <p>A click-to-reveal control.</p> </div> What is a disclosure widget? A click-to-reveal control, like details from Part 1. top layer - no z-index needed popovertarget="hint" points at id="hint" - click toggles the overlay open and closed.
🔍 Click to zoom - two attributes replace the dropdown library, no script required
<button popovertarget="hint">
  What is a disclosure widget?
</button>

<div id="hint" popover>
  <p>A click-to-reveal control, like the
  details element from Part 1.</p>
</div>

The overlay renders in the browser's top layer - a rendering level above everything on the page. No z-index, no positioning fights: the browser guarantees the popover paints on top.

Self-studypopover vs dialog - picking the right box2 min read▶
Questionpopoverdialog (modal)
Blocks the rest of the page?No - page stays liveYes - background goes inert
Click outside to close?Yes, built in (light dismiss)No - explicit close or Esc
Needs JavaScript to open?No - popovertarget buttonYes - one showModal() call
Best forTooltips, menus, hintsConfirmations, blocking forms

Rule of thumb: if the user must deal with it before continuing, dialog. If it is helpful but skippable, popover. Bonus attribute: popovertargetaction="show" or "hide" makes a button one-directional instead of a toggle - handy for a dedicated ✕ button inside the popover.

Part 4 · covers web.dev "Focus" + inline text extras

Focus and tabindex - interactivity for every hand core

Everything above responds to a mouse. Focus is how it all responds to a keyboard: one element at a time holds the focus, Tab moves it forward, Shift+Tab moves it back, Enter or Space activates. Power users, screen-reader users, and anyone whose mouse just died all live on this system.

LiveTab order follows source order3 min▶

Interactive elements - links, buttons, form fields, summary - are focusable automatically, in the order they appear in your HTML. Write the source in a sensible reading order and keyboard navigation is correct with no extra work. This is why CSS tricks that visually reorder content can quietly break keyboard flow: the eye sees one order, Tab follows another.

<p>Press Tab to walk this form:</p>
<input type="text" placeholder="1st stop: name">
<input type="email" placeholder="2nd stop: email">
<p>A link is a stop too:
<a href="#">3rd stop</a></p>
<button>4th stop: submit</button>
Self-studytabindex: the three values that matter3 min read▶
  • tabindex="0" - adds a non-interactive element into the natural tab order, at its source position. For the rare custom widget that must be focusable.
  • tabindex="-1" - focusable by script or by link target, but skipped by Tab. This is how skip links land focus on a heading.
  • tabindex="1" or any positive number - a trap, do not use. Positive values hijack the whole page's tab order ahead of everything else, and every future edit has to re-solve the numbering puzzle. Fix the source order instead; that is where tab order belongs.

And the companion rule from CSS-land, stated early because it ruins lives: never remove the focus outline without replacing it. An invisible focus is a keyboard user navigating blind.

The skip-link pattern<a href="#main" class="skip-link">Skip to content</a>
...navigation with 40 links...
<main id="main" tabindex="-1">...</main>

First focusable element on the page, usually hidden until focused: one Tab, one Enter, and a keyboard user leaps over your entire navigation. Every serious site has one - check for it with a single Tab press on any government website.

Self-studyBonus vocabulary: the inline elements this page just used2 min read▶

You met them in the wild above - now the formal introductions, from HTML's "other inline text elements" drawer:

  • <kbd> - a keyboard input, like Tab and Esc throughout this part. Semantics for "the user types this".
  • <mark> - highlighted relevance, like a search-result match. Meaning: "notice this here", not "this is bold".
  • <abbr title="Chief Data and AI Officer">CDAIO</abbr> - an abbreviation whose title attribute holds the expansion; hover one to see the built-in tooltip (popover's tiny ancestor).
  • <small> - fine print: legal lines, side comments, the footnote voice.
Homework

Try it yourself - this week ◐ 20-30 min total

Source material

Official sources covered

This dive teaches the working content of these chapters from web.dev Learn HTML and the MDN Popover API docs. Their interactive checks stay with the originals - our exercises are equivalents, not copies.

✓web.dev: Details and summaryPart 1 - disclosure, open, exclusive accordions via name
✓web.dev: DialogPart 2 - open attribute live, modal half honestly labeled JS
✓MDN: Popover API (HTML attributes)Part 3 - popover + popovertarget; support flagged (2024+ browsers)
✓web.dev: FocusPart 4 - tab order, tabindex 0 / -1 / positive, skip links
✓web.dev: Other inline text elementswoven in - kbd, mark, abbr, small met in context
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · You want an FAQ where opening one question closes the others. The pure-HTML move is...

Shared name = exclusive group, like radio buttons. There is no accordion element, and onclick means writing the JavaScript this whole page exists to avoid.

2 · A "delete your account?" confirmation that must be answered before anything else - which tool?

Must-answer means modal: inert background, trapped focus, Esc handling. Popovers light-dismiss on any outside click - exactly wrong for a decision you cannot skip.

3 · A teammate sets tabindex="3" to "fix" the tab order. What is the professional response?

Positive values jump ahead of every natural stop on the page and turn future edits into a numbering puzzle. Tab order lives in source order; tabindex 0 joins the natural order, it never reorders it.

Deep dive 6.1 cheat sheet · pin this

details / summarysummary first child = clickable line; rest hidden until opened. open attribute = starts expanded. Zero JS.
Exclusive accordionSame name attribute on sibling details = opening one closes the rest. 2024+ browsers; older ones just allow multi-open.
dialogWithout open: invisible, waiting. With open: non-modal in-page box. Modal (backdrop, focus trap, Esc) = one showModal() JS line.
form method="dialog"A form inside a dialog that closes it on submit - the no-script closing half of the pattern.
popoverpopover attribute on the overlay + popovertarget on a button. Toggle, light dismiss, top layer - all free. 2024+ browsers.
popover vs dialogSkippable extras = popover. Must-answer interruptions = modal dialog. Blocking is the dividing line.
Focus basicsTab walks interactive elements in source order. Sensible source = correct keyboard flow, free.
tabindex0 = join natural order. -1 = focusable by script/target only. Positive = trap, never use. Never hide the focus outline.