v154 · dom · popover & dialog · enabled by default
Light dismiss improvements for popovers and dialogs
Chrome 154 changes how the browser decides that you clicked outside an open popover or a <dialog closedby="any">: light dismiss is now driven by the click event instead of separate pointerdown/pointerup tracking. Two long-standing bugs disappear as a result — scrolling gestures on touch screens no longer light-dismiss, and right clicks no longer light-dismiss. No HTML or JavaScript surface is added or removed; existing pages simply stop being dismissed by input sequences that never produce a click.
Chrome 154 ships this enabled by default (runtime flag LightDismissFromClick, status stable in the Chromium runtime_enabled_features.json5, verified 2026-10-04), but the specification changes are still open pull requests: whatwg/html#11536 and w3c/pointerevents#460 (both open as of 2026-10-04), and the ChromeStatus standardization status is “Proposed”. Algorithm details quoted below come from those PRs verbatim and may still be refined before merging.
at a glance
| Shipped in | Chrome 154 (desktop, Android, WebView) — filed under “Enabled by default” in the milestone=154 listing; dev-trial stage recorded from Chrome 145 |
|---|---|
| Runtime flag | LightDismissFromClick, status stable — Chromium comment: “Makes light dismiss for popovers and dialogs use the click event instead of pointerdown and pointerup in order to fix light dismiss bugs for touch inputs” (runtime_enabled_features.json5, verified 2026-10-04) |
| Spec changes | whatwg/html PR #11536 (rewrites the light dismiss algorithms; open) + w3c/pointerevents PR #460 (invokes them from click dispatch; open), against the Popover light dismiss and Dialog light dismiss sections |
| Explainer | w3c/pointerevents issue #542 — “Add context for popover light dismiss to click event” |
| Tracking bugs | crbug 408010435 (touch scrolling triggers light dismiss) · crbug 425579196 (right click triggers light dismiss) — issue pages sit behind a sign-in wall; descriptions here come from the ChromeStatus record’s motivation field |
| Intent to Ship | No intent thread is recorded on the ChromeStatus record (all intent_thread_url fields null, queried 2026-10-04) — no public blink-dev link can be given yet |
| ChromeStatus | 6209615938322432 — Light dismiss improvements for popovers and dialogs |
syntax
Nothing changes in the markup you write. Light dismiss remains opt-in through the same two attributes, whose definitions in the current HTML Standard read (verbatim, fetched 2026-10-04):
“Light dismiss” means that clicking outside of a popover whose
popoverattribute is in the Auto or Hint state will close the popover. This is in addition to how such popovers respond to close requests.“Light dismiss” means that clicking outside of a
dialogelement whoseclosedbyattribute is in the Any state will close thedialogelement.
<!-- Popovers: popover="auto" (or "hint") opts into light dismiss -->
<div id="menu" popover="auto">…</div>
<!-- Dialogs: closedby="any" opts into light dismiss -->
<dialog closedby="any">…</dialog>
The closedby keyword table from the spec: any — “Close requests or clicks outside close the dialog”; closerequest — “Close requests close the dialog”; none — “No user actions automatically close the dialog”. What Chrome 154 changes is the algorithms behind that behavior, documented below.
what changed
Before Chrome 154, the HTML Standard’s light dismiss algorithms listen to each trusted pointerdown and pointerup PointerEvent separately: on pointerdown the document stores a “popover pointerdown target” / “dialog pointerdown target”, and on pointerup it compares targets and hides. Any pointer-up outside the popover could dismiss — including the pointer sequences of a touch scroll or a right click, neither of which is a click. whatwg/html PR #11536 deletes that tracking (both “pointerdown target” document states are removed) and replaces it with algorithms evaluated once, from click-event dispatch:
| Input sequence | Before Chrome 154 | Chrome 154+ |
|---|---|---|
| Click/tap outside | Light dismiss | Light dismiss (unchanged) |
| Touch scroll gesture starting outside | Could light dismiss | No dismiss — a canceled scroll never produces a click |
| Right click outside | Could light dismiss | No dismiss — a right click fires auxclick, not click, and the pointerevents hook runs only for click |
| Press outside, release over the popover (or vice versa) | No dismiss (pointerdown/up target mismatch) | No dismiss (pointerdown/up data mismatch, now carried on the click) |
The scroll and right-click rows restate the ChromeStatus summary: “The fixed bugs include making scrolling gestures on touch screens no longer trigger light dismiss and making right clicks no longer trigger light dismiss. The underlying mechanism of this change is that the browser will use click events to trigger light dismiss instead of a combination of pointerdown and pointerup events.” The explainer adds a third motivation: “pointerdown is skipped on touch devices in blink and webkit unless there is a pointerdown event listener”, which broke light dismiss entirely in some engines, and the old design “is replicating a form of click detection which would better be served by listening to the existing click event”.
Observable timing consequence: light dismiss now happens as part of click processing. Per pointerevents PR #460, “run light dismiss activities” executes in the Event dispatch section immediately before the click event is dispatched to its target, so by the time your own click listener runs, a light-dismissed popover has already begun closing (its toggle/beforetoggle sequence fires around the click rather than at pointer-up time).
the rewritten popover algorithm
New signature and steps, quoted from the PR #11536 diff (fetched 2026-10-04). The old algorithm took “a PointerEvent event” and branched on pointerdown/pointerup; the new one receives both targets at once:
To light dismiss open popovers, given a Node pointerDownTarget and a Node pointerUpTarget:
- Let document be pointerDownTarget’s node document.
- Let pointerDownPopover be the result of running topmost clicked popover given pointerDownTarget.
- Let pointerUpPopover be the result of running topmost clicked popover given pointerUpTarget.
- If pointerDownPopover is not pointerUpPopover, then return.
- Let endpointIsHint be true if document’s showing hint popover list contains pointerDownPopover; otherwise false.
- Run hide popover stack until given document, pointerDownPopover, Hint, false, and true.
- Let autoEndpoint be pointerDownPopover.
- If endpointIsHint is true, then set autoEndpoint to document’s hint stack parent. (Spec note: “This means, if a hint popover is clicked, auto popovers are closed, except those that are parent to the clicked hint popover.”)
- Run hide popover stack until given document, autoEndpoint, Auto, false, and true.
Deleted alongside: the “Every Document has a popover pointerdown target” state, the isTrusted assert (trust is now guaranteed by the caller — the browser’s own click dispatch), and the early-return when no auto/hint popover is showing (subsumed by the stack walk). The helper algorithms it calls — topmost clicked popover, popover stack position, nearest inclusive target popover — are unchanged by this PR; their current text is in the live spec section.
the rewritten dialog algorithm
Same pattern for <dialog closedby="any">, with coordinates added so backdrop clicks can be told apart from clicks on the dialog itself. Quoted from the PR #11536 diff:
To light dismiss open dialogs, given a Node pointerDownTarget, a number pointerDownX, a number pointerDownY, a Node pointerUpTarget, a number pointerUpX, and a number pointerUpY:
- Let document be pointerDownTarget’s node document.
- If document’s open dialogs list is empty, then return.
- Let pointerDownDialog be the result of running nearest clicked dialog given pointerDownTarget, pointerDownX, and pointerDownY.
- Let pointerUpDialog be the result of running nearest clicked dialog given pointerUpTarget, pointerUpX, and pointerUpY.
- If pointerDownDialog is not pointerUpDialog, then return.
- Let topmostDialog be the last element of document’s open dialogs list.
- If pointerDownDialog is topmostDialog, then return.
- If topmostDialog’s computed closed-by state is not Any, then return.
- Assert: topmostDialog’s close watcher is not null.
- Request to close topmostDialog’s close watcher with false.
Nearest clicked dialog is re-signed from “given a PointerEvent event” to “given a Node target, a number clientX, and a number clientY”, and its hit test now uses CSSOM View’s get the bounding box (a dependency the PR adds to the HTML spec’s CSSOM View references). The spec note explains why coordinates are needed at all: “a pointer event that hits the ::backdrop pseudo element of a dialog will result in event having a target of the dialog element itself. This could be improved if the pointer events spec passed the ::backdrop pseudo-element directly instead of the dialog element.” The document’s “dialog pointerdown target” state is deleted. Closing still goes through the dialog’s close watcher, so cancel/close event semantics are untouched.
when the browser runs light dismiss
The entry point, run light dismiss activities, keeps its two steps (popovers first, then dialogs) but now takes the full pointer context: “given a Node pointerDownTarget, a number pointerDownX, a number pointerDownY, a Node pointerUpTarget, a number pointerUpX, and a number pointerUpY”. Who calls it moves from prose (“when the user clicks or touches anywhere on the page”) into normative Pointer Events dispatch steps. Quoted from the w3c/pointerevents PR #460 diff (inserted into §Event dispatch, immediately before “Dispatch event to target”):
If event is a click event, then:
- Let pointerDownTarget, pointerDownX, pointerDownY, pointerUpTarget, pointerUpX, and pointerUpY each be null.
- If event had corresponding
pointerdownandpointerupevents preceding it, then: set pointerDownTarget/pointerDownX/pointerDownY to the correspondingpointerdownevent’s event target,clientX, andclientY, and pointerUpTarget/pointerUpX/pointerUpY to the correspondingpointerupevent’s event target,clientX, andclientY. - Otherwise: set both targets to event’s event target and both coordinate pairs to event’s
clientXandclientY. (This covers clicks with no preceding pointer sequence, such as keyboard-synthesized activations.) - Run light dismiss activities given pointerDownTarget, pointerDownX, pointerDownY, pointerUpTarget, pointerUpX, and pointerUpY.
Because the hook is gated on “event is a click event”, an auxclick (right/middle button) never reaches light dismiss, and a touch scroll — whose pointer sequence is canceled and never produces a click — cannot dismiss anything. Note the parenthetical in step 3 is gendn’s reading of “Otherwise”; the PR text itself does not enumerate which clicks lack pointer events.
examples
Opting in is unchanged — the improvement is what stops happening. On a Chrome 154 touch screen, scrolling the page behind this menu no longer closes it, and right-clicking the page no longer closes it:
<button popovertarget="filters">Filters</button>
<div id="filters" popover="auto">
<!-- Chrome 154: survives touch-scroll and right-click outside;
still closes on a real click/tap outside or Esc -->
</div>
<dialog id="prefs" closedby="any">
<form method="dialog"><button>Done</button></form>
</dialog>
<script>
document.getElementById('prefs').showModal();
// closedby="any": a click on ::backdrop light-dismisses via the
// dialog's close watcher, so 'cancel' then 'close' fire as before.
</script>
If you worked around the old bugs — for example re-opening a popover from a contextmenu handler because right click dismissed it, or suppressing pointerup to stop scroll-dismiss — those workarounds become dead code in Chrome 154 and can be removed behind a version check or left to no-op.
No live demo is embedded: the matching Chrome Platform Showcase route (/v154/light-dismiss-improvements-for-popovers-and-dialogs/) returned HTTP 404 when checked on 2026-10-04 (demo not built yet). When it lands it will appear under the showcase index.
browser compatibility
BCD html/global_attributes.json and BCD html/elements/dialog.json have no entry for this click-based light dismiss change (checked 2026-10-04) — BCD tracks the base features, below. The change’s own ship data is therefore from ChromeStatus, labelled interim:
| Surface | Chrome | Edge | Firefox | Safari |
|---|---|---|---|---|
| Click-based light dismiss (this change) — interim, ChromeStatus | 154 (desktop, Android, WebView) | Unknown — not yet recorded | No signal | No signal |
popover attribute (BCD) | 114 | mirror | 125 | 17 |
<dialog closedby> (BCD) | 134 | mirror | 141 | preview |
Baseline: popover is newly available (low date 2025-01-27, all engines); dialog-closedby is limited (no Safari release; Apple’s position is “support” per WebKit/standards-positions#329). The explainer notes implementation bugs were filed on Chromium, Gecko and WebKit for the click-based change, but no Firefox/Safari release carries it yet and ChromeStatus records “No signal” views from both (queried 2026-10-04). Feature-detecting the new behavior from script is not possible — there is no API surface to probe; treat it as a progressive quality-of-implementation improvement.
security and privacy
Light dismiss only ever reacts to real user input: the pre-154 algorithms assert the PointerEvent’s isTrusted is true, and the new design preserves that guarantee structurally — the pointerevents hook runs inside the browser’s own event-dispatch steps for the click it is constructing, so synthetic element.click() or dispatched MouseEvents cannot trigger it. Dismissal of a dialog still routes through its close watcher (“Request to close … with false”), keeping close-watcher abuse protections and cancel-ability intact. No new data is exposed to pages: the pointer targets and coordinates the algorithms consume are the same ones already delivered via pointerdown/pointerup/click events.
specifications
| Specification | Status |
|---|---|
| HTML Standard PR #11536 — light dismiss via click events | Open PR (checked 2026-10-04); ChromeStatus maturity: “Working draft” |
| Pointer Events PR #460 — “Add popover light dismiss integration” | Open PR (checked 2026-10-04) |
| HTML Standard §6.12.2 Popover light dismiss · §4.11.5 Dialog light dismiss | Current (pre-change) normative text these PRs amend |
see also
- MDN: Popover API and MDN: popover global attribute (base feature; the click-based mechanism is not yet described there, checked 2026-10-04)
- MDN: HTMLDialogElement.closedBy and MDN: <dialog>
- Explainer: w3c/pointerevents#542 — Add context for popover light dismiss to click event
- whatwg/html#10905 — popovers broken in contextmenu event listeners (fixed by PR #11536)
- chrome-platform-showcase (feature demo route not built yet; 404 on 2026-10-04)
- ChromeStatus — Light dismiss improvements for popovers and dialogs (6209615938322432)