Skip to content

[core-docs] Remove the globalSelector stylis middleware - #49029

Draft
Janpot wants to merge 2 commits into
mui:masterfrom
Janpot:code-infra/remove-global-selector
Draft

[core-docs] Remove the globalSelector stylis middleware#49029
Janpot wants to merge 2 commits into
mui:masterfrom
Janpot:code-infra/remove-global-selector

Conversation

@Janpot

@Janpot Janpot commented Aug 25, 2026

Copy link
Copy Markdown
Member

The docs' emotion cache ran a globalSelector stylis middleware that strips any class in front of :where(/:is( — a workaround for emotion-js/emotion#2836. It can't tell the buggy glued prefix from an intentional one, so any component style using &:where(…)/&:is(…) silently loses its scoping on mui.com (see #49028), and docs rendering diverged from every other consumer of the same components.

theme.applyStyles already dodges the emotion bug by prefixing with *. This converts all bare ':where(…) &' keys to that form, rewrites Paper's ':is(a&), :is(button&)' as '&:is(a, button)', and deletes the middleware plus an unimported copy. Verified by prod-building the docs before and after and pixel-comparing a sample of pages in light and dark: identical except the rotating ad slot. mui-x and base-ui docs don't use the bare form in their own sources (mui-x's DemoPropsForm/PlaygroundCard do and need the same conversion before the next core-docs bump).

Two visible changes on mui.com, both restorations rather than regressions: component styles like ListItemText's :where(& .MuiListItemText-primary) get their scoping back, and the dark-mode hover shadow on outlined Paper links comes back to life — on master the middleware-era :is(a&) inside applyDarkStyles expanded to an invalid :is() argument that matched nothing.

@code-infra-dashboard

code-infra-dashboard Bot commented Aug 25, 2026

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/material 0B(0.00%) 0B(0.00%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@Janpot Janpot added the scope: docs-infra Involves the docs-infra product (https://www.notion.so/mui-org/b9f676062eb94747b6768209f7751305). label Aug 25, 2026
LukasTy

This comment was marked as resolved.

Janpot added 2 commits August 25, 2026 12:06
The middleware compensated for emotion gluing the generated class onto
nested selector keys that start with `:where(`/`:is(`
(emotion-js/emotion#2836) by stripping any class it found in front of
those pseudo-classes. That regex cannot tell the buggy glued prefix from
an intentional one, so any component style using `&:where(...)`/`&:is(...)`
loses its scoping in the docs -- most recently turning a TimelineItem
`::before` rule into one that matched nearly every element on mui.com.
It also only ran in the docs' emotion cache, so docs rendering silently
diverged from every other consumer of the same components.

The workaround end users already rely on covers the docs too:
`theme.applyStyles` prefixes the selector with `*`, which emotion passes
through untouched. All bare `':where(...) &'` keys -- the six typography
ones plus the template-literal form in `MarkdownElement` and the ApiPage
tables/lists -- now use that same `*:where()` shape, and the Paper
`':is(a&), :is(button&)'` keys become `'&:is(a, button)'`: the same
matched set and the same specificity the middleware used to produce,
without depending on it.

Verified by building the docs before and after and pixel-comparing a
sample of pages (home, markdown docs, component pages, API pages, and a
template, in light and dark): identical except the rotating ad slot.

`docs/src/modules/utils/globalSelector.ts` was an unimported copy of the
same middleware; deleted with it.
The one bare `:where()` key the sweep missed -- it lives under docs/data,
which the glob didn't cover. Without the middleware it compiled to a
self-glued selector that could never match, leaving the dark-mode switch
track on the Default theme viewer at its light-mode grey.
@Janpot
Janpot force-pushed the code-infra/remove-global-selector branch from 4f92815 to ace0644 Compare August 25, 2026 10:07
@Janpot

Janpot commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

1. DefaultTheme.js — Fixed and pushed. It sat in docs/data, which the sweep didn't cover. Verified on the Default theme viewer: dark track is back to rgb(87, 99, 117), light unchanged. Re-swept the whole repo including docs/data this time — that was the last bare occurrence.

2. Paper :is dark branch — Agreed it's a revival, not a no-op, and I've updated the description. One nuance from measuring it: on the sponsor cards the revived rule is immediately outranked by &[href]:hover (0,3,0 vs 0,2,1), which sets its own hover shadow — the computed shadow on hover is identical on master and this branch (rgba(0, 59, 117, 0.6) 0 2px 8px). Since every Paper-as-link in the docs chrome carries an href, and demos render under the demo theme rather than the branding theme, the revived rule currently has no visible instance — consistent with the before/after screenshots being pixel-identical outside the ad slot. It only becomes visible if something later renders an outlined branding-theme Paper as <button> or an href-less <a>.

3. mui-x — Done in mui/mui-x#23419, same * prefix for DemoPropsForm and PlaygroundCard, referencing this PR so it can land before the next core-docs bump.

@Janpot
Janpot requested a review from LukasTy August 25, 2026 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: docs-infra Involves the docs-infra product (https://www.notion.so/mui-org/b9f676062eb94747b6768209f7751305).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants