Skip to content

Technical review: Chrome 149 User action pseudo-class top-layer boundary#44615

Open
chrisdavidmills wants to merge 2 commits into
mdn:mainfrom
chrisdavidmills:User-action-pseudo-class-top-layer-boundary
Open

Technical review: Chrome 149 User action pseudo-class top-layer boundary#44615
chrisdavidmills wants to merge 2 commits into
mdn:mainfrom
chrisdavidmills:User-action-pseudo-class-top-layer-boundary

Conversation

@chrisdavidmills

Copy link
Copy Markdown
Contributor

Description

Chrome 149 adds User Action pseudo-class top-layer matching boundary behavior: see https://chromestatus.com/feature/6296574159355904.

This PR attempts to document this behavior. It was somewhat tricky to figure out how to approach this. I have not added any details to the :hover, :active, and :focus-within pages; I'm intending to add a link to the explanatory section to their BCD entries, rather than explaining it on each page.

Motivation

Additional details

Related issues and pull requests

@chrisdavidmills chrisdavidmills requested review from a team as code owners July 1, 2026 14:57
@chrisdavidmills chrisdavidmills requested review from dipikabh and hamishwillee and removed request for a team July 1, 2026 14:57
@github-actions github-actions Bot added Content:CSS Cascading Style Sheets docs Content:Learn Learning area docs Content:Glossary Glossary entries labels Jul 1, 2026
@chrisdavidmills chrisdavidmills changed the title Chrome 149 User action pseudo-class top-layer boundary Technical review: Chrome 149 User action pseudo-class top-layer boundary Jul 1, 2026
@github-actions github-actions Bot added the size/m [PR only] 51-500 LoC changed label Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@mfreed7 mfreed7 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, just some small comments.

The drop-down picker can be targeted using the {{cssxref("::picker()", "::picker(select)")}} pseudo-element. As mentioned earlier, the picker contains everything inside the `<select>` element that isn't the button and the `<selectedcontent>`. In our example, this means all the `<option>` elements and their contents.

First of all, the picker's default black {{cssxref("border")}} is removed:
When the picker is opened, its contents (contained within the `::picker(select)` pseudo-element) are promoted to the {{glossary("top layer")}}, as if they were contained inside a [popover](/en-US/docs/Web/API/Popover_API). This ensures that the picker is displayed on top of elements surrounding the `<select>` element.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "as if they were", I'd say something like "because the ::picker() is a popover" or something similar.

Also, in addition to displaying on top of other elements, it also ensures the picker plays nicely with other popovers on the page, e.g. stacking with them or closing unrelated ones.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, updated to:

When the picker is opened, its contents (contained within the ::picker(select) pseudo-element) are promoted to the {{glossary("top layer")}}, because the picker is a popover. This ensures that the picker displays on top of other elements, and interacts gracefully with other popovers on the page (for example, closing unrelated ones that are already open).

When the picker is opened, its contents (contained within the `::picker(select)` pseudo-element) are promoted to the {{glossary("top layer")}}, as if they were contained inside a [popover](/en-US/docs/Web/API/Popover_API). This ensures that the picker is displayed on top of elements surrounding the `<select>` element.

> [!NOTE]
> The select picker is also subject to [top-layer ancestor matching boundary](/en-US/docs/Web/CSS/Reference/Selectors/Pseudo-classes#top-layer_ancestor_matching_boundary) behavior, which ensures that {{cssxref(":hover")}}, {{cssxref(":active")}}, or {{cssxref(":focus-within")}} styles applied to the picker's descendants won't spill out of the picker.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it's not exactly styles applied to the pickers descendants, because in that case, there's nothing to spill. I.e. the CSS selector selects something within the picker.

The problem this solves is that select:hover won't match the in-page element while the picker is hovered.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, makes sense. I've updated the note to:

Note

The select picker is also subject to top-layer ancestor matching boundary behavior, which ensures that {{cssxref(":hover")}}, {{cssxref(":active")}}, or {{cssxref(":focus-within")}} styles applied to the <select> will only match the picker's descendants while the picker is interacted with, not the <select> itself.


Note how when you hover the `<select>` or the popover toggle button, all ancestors of those elements will get the border. However, when you open the select picker or the popover and hover one of their descendants, the matching stops at the top-layer ancestor (the select picker or popover themselves).

This behavior stops component styles set on those pseudo-classes from spilling out of a top layer component into the surrounding page, which can cause a user interface to look broken.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:CSS Cascading Style Sheets docs Content:Glossary Glossary entries Content:Learn Learning area docs size/m [PR only] 51-500 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants