← Chrome 149 reference

v149 · css · behavior change

User-Action Pseudo-Class Top-Layer Boundary

Chrome 149 implements the CSS Selectors Level 4 rule that :hover, :active, and :focus-within propagate up the ancestor chain only as far as the nearest top-layer element — so interacting with a popover or dialog no longer accidentally triggers hover/active styles on elements behind it in the regular document flow.

Behaviour change If you have CSS rules that rely on :hover or :focus-within propagating from a popover or dialog into its DOM ancestors, those rules will stop matching in Chrome 149. This is the correct interoperable behaviour per the spec.

at a glance

Shipped inChrome 149 / Edge 149
StatusEnabled by default
FlagNone
SpecCSS Selectors Level 4 — user-action pseudo-class propagation rules
ChromeStatus6296574159355904 — User action pseudo class top layer boundary

what changed and why

The :hover, :active, and :focus-within pseudo-classes propagate upward through the DOM: when an element matches, all its ancestors match too. This makes sense for regular document flow — hovering a button inside a card should allow you to style the card.

Top-layer elements (popovers, <dialog>s, and fullscreen elements) are special: although they exist in the DOM tree, they are rendered in a separate stacking layer entirely above the rest of the page. When a button inside a popover is hovered, propagating :hover all the way up to the <body> or a container <section> makes no visual sense — those elements are displayed behind the popover and the user is not interacting with them.

CSS Selectors Level 4 specifies that user-action pseudo-class propagation stops at the first top-layer element in the ancestor chain. Chrome 149 implements this rule.

Source: blink-dev Web-Facing Change PSA.

the rule

Given this DOM structure:

<main>
  <div popover id="panel">
    <button>Click me</button>
  </div>
</main>

When the user hovers the <button>:

Element Before Chrome 149 Chrome 149+
<button> :hover matches :hover matches
<div popover> (top-layer element) :hover matches :hover matches
<main> (ancestor of the top-layer element) :hover matches :hover does NOT match
<body>, <html> :hover matches :hover does NOT match

The same boundary rule applies to :active and :focus-within. It applies to all top-layer promotion types: popover attribute, <dialog> (opened with showModal()), and requestFullscreen().

Source: CSS Selectors Level 4 and blink-dev PSA.

example

CSS that breaks in Chrome 149

/* This selector will NO longer match when the user hovers a button
   inside a popover that is a descendant of .sidebar. */
.sidebar:hover {
  background: var(--hover-tint);
}

CSS that still works

/* Hovering within the popover itself still sets :hover on the popover. */
[popover]:hover {
  box-shadow: 0 4px 20px rgba(0,0,0,.2);
}

/* Hover on regular (non-top-layer) ancestors still propagates. */
.card:has(button:hover) {
  border-color: var(--accent);
}

Feature detecting the new behaviour

// No CSS feature query for this change. Use the :has() workaround
// for styling ancestors of hovered children, which works regardless.
.container:has(:popover-open:hover) {
  /* applies when an open popover inside .container is hovered */
}

affected pseudo-classes

:hoverStops propagating at the first top-layer ancestor.
:activeStops propagating at the first top-layer ancestor.
:focus-withinStops propagating at the first top-layer ancestor.
:focus, :focus-visibleNot affected — these match the focused element itself, not its ancestors.

browser support

Chrome149
Edge149 (Chromium)
FirefoxStatus unknown (as of Chrome 149)
SafariStatus unknown (as of Chrome 149)
Source: chromestatus, May 2026.

see also