[POC] Use ThemeCSSProvider as replacement for ThemeProvider#48652
[POC] Use ThemeCSSProvider as replacement for ThemeProvider#48652silviuaavram wants to merge 40 commits into
Conversation
Deploy previewBundle size
Check out the code infra dashboard for more information about this PR. |
There was a problem hiding this comment.
Pull request overview
Adds a new @mui/tailwind package as a POC integration layer between MUI’s CSS variables (from CssVarsProvider) and Tailwind (v3 preset + v4 CSS directives), and wires it into the docs with a new integration page and demos.
Changes:
- Introduce
@mui/tailwind(Tailwind v3 preset + state-variant plugin + Tailwind v4v4.csstoken mapping). - Update docs to consume
@mui/tailwind/v4.cssand add a new “Tailwind components” integration page + demos. - Adjust repo config/deps to include the new workspace package and remove
postcss-importfrom docs.
Reviewed changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Adds TS path aliases for the new @mui/tailwind package. |
| pnpm-lock.yaml | Adds workspace link for @mui/tailwind and updates lock entries (incl. removal of postcss-import). |
| packages/mui-tailwind/src/v4.css | Tailwind v4 CSS integration: breakpoints, token mapping, utilities, and mui-* variants. |
| packages/mui-tailwind/src/preset.js | Tailwind v3 preset exposing MUI token mappings + plugin registration. |
| packages/mui-tailwind/src/plugin.js | Tailwind plugin adding mui-{state} / mui-not-{state} variants. |
| packages/mui-tailwind/README.md | Links to the docs integration page for setup and demos. |
| packages/mui-tailwind/package.json | Declares the new package metadata and entrypoint exports. |
| docs/postcss.config.js | Removes postcss-import from the docs PostCSS pipeline. |
| docs/pages/material-ui/integrations/tailwindcss/tailwindcss-components.js | Adds a new docs route for the Tailwind components page. |
| docs/pages/global.css | Imports @mui/tailwind/v4.css and updates layer ordering for Tailwind/MUI. |
| docs/package.json | Adds @mui/tailwind workspace dependency; removes postcss-import. |
| docs/data/material/integrations/tailwindcss/TailwindFilterChips.tsx | New TS demo using mui-selected: Tailwind variant on MUI ToggleButtons. |
| docs/data/material/integrations/tailwindcss/TailwindFilterChips.js | JS version of the filter chips demo. |
| docs/data/material/integrations/tailwindcss/TailwindDisabledState.tsx | New TS demo using mui-disabled: variant with Slider/Switch. |
| docs/data/material/integrations/tailwindcss/TailwindDisabledState.js | JS version of the disabled-state demo. |
| docs/data/material/integrations/tailwindcss/tailwindcss-v4.md | Points readers to the new @mui/tailwind package page. |
| docs/data/material/integrations/tailwindcss/tailwindcss-components.md | New docs page describing @mui/tailwind setup (v3 + v4) and demos. |
| docs/data/material/integrations/tailwindcss/TailwindCard.tsx.preview | Preview snippet for the Tailwind job card demo. |
| docs/data/material/integrations/tailwindcss/TailwindCard.tsx | New TS Tailwind-styled job card demo built from MUI components. |
| docs/data/material/integrations/tailwindcss/TailwindCard.js | JS version of the Tailwind job card demo (+ PropTypes). |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
39e2999 to
8b43394
Compare
0c748fd to
da7fa88
Compare
| react(), | ||
| ], | ||
| resolve: { | ||
| alias: [ |
There was a problem hiding this comment.
You should remove these aliases and test against the build folders instead. These aliases tend to turn what's supposedly 3rd party code for bundlers into 1st party code. Which operates under different rules in some runtimes. In the past I've also masks module graph issues around csj/esm.
There was a problem hiding this comment.
sure, let me check.
There was a problem hiding this comment.
Yep, the testing will be a bit more complicated, as we would need to build the packages first, but at least we will test the real output of the libraries. @brijeshb42 I know you were creating something like this when we had pigment css apps, any suggestion for a specific setup?
There was a problem hiding this comment.
As far as I can tell, we don't have any build-time integration here. Pigment needed to be integrated with the bundler, so I had to build with watch-mode (using tsup at the time) since we needed the transpiled code to be called in next.config.ts or vite.config.ts.
So the current setup should work whether using alias to source of build folder directly.
@mui/tailwind will need this setup of building in watch mode or manual builds to be used in docs.
e1a745e to
f739cd0
Compare
| // This is exactly what non-Emotion users configure in their own bundler. | ||
| { | ||
| find: '@mui/styled-engine', | ||
| replacement: path.resolve(MONOREPO_ROOT, 'packages/mui-styled-engine-noop/src'), |
There was a problem hiding this comment.
We should keep this test as close to end-user environment as possible. So let's alias to the build folder
| replacement: path.resolve(MONOREPO_ROOT, 'packages/mui-styled-engine-noop/src'), | |
| replacement: '@mui/styled-engine-noop', |
and remove 'treat-js-files-as-jsx'.
There was a problem hiding this comment.
sure, let me do that!
There was a problem hiding this comment.
I can't make this work. It only works with:
replacement: path.resolve(MONOREPO_ROOT, 'packages/mui-styled-engine-noop/build'),
Using replacement: '@mui/styled-engine-noop', as you suggested gives this issue, and I was not able to solve it.
[plugin:vite:import-analysis] Failed to resolve import "@mui/styled-engine" from "../../packages/mui-system/build/index.mjs". Does the file exist?
/Users/silviuavram/Documents/git-repos/mui/material-ui/packages/mui-system/build/index.mjs:9:53
7 | */
8 | import _formatErrorMessage from "@mui/utils/formatMuiErrorMessage";
9 | export { css, keyframes, StyledEngineProvider } from "@mui/styled-engine";
| ^
10 | export { default as GlobalStyles } from "./GlobalStyles/index.mjs";
11 | export { default as borders } from "./borders/index.mjs";
There was a problem hiding this comment.
Even when you ran pnpm release:build?
There was a problem hiding this comment.
Yes. Maybe you can try as well, I can't get it to go.
[plugin:vite:import-analysis] Failed to resolve import "@mui/styled-engine" from "../../packages/mui-system/build/index.mjs". Does the file exist?
/Users/silviuavram/Documents/git-repos/mui/material-ui/packages/mui-system/build/index.mjs:9:53
7 | */
8 | import _formatErrorMessage from "@mui/utils/formatMuiErrorMessage";
9 | export { css, keyframes, StyledEngineProvider } from "@mui/styled-engine";
| ^
10 | export { default as GlobalStyles } from "./GlobalStyles/index.mjs";
11 | export { default as borders } from "./borders/index.mj
There was a problem hiding this comment.
Ah yes, the @mui/styled-engine-noop is not reachable from @mui/system as it's not a dependency, unlike @mui/styled-engine. This shouldn't be a problem on our user's end, but for use we could remediate by installing @mui/styled-engine-noop as a top-leveel dependency, then it becomes reachable from all packages
pnpm add @mui/styled-engine-noop@workspace:* -D -w
Not sure we should do this though. It's just as much not like end-user setup as the current situation is
|
Can you clarify this part:
What do you mean sx works in one case but not the other? Should sx not only non-emotion setup? Maybe replace "For css example in experiments" with "When using emotion (experiments)" |
mnajdova
left a comment
There was a problem hiding this comment.
Ok maybe I need a explanation why we have mui.default? And the styles for Slider are under the mui.components layer?
mnajdova
left a comment
There was a problem hiding this comment.
Ok, so we have now the CSS vars provider that adds DOM element and ThemeScope provider to make the portaled element be able to read this value. This means that we will need to update all portaled elements to read this provider, no?
| const rtl = resolved.direction === 'rtl'; | ||
|
|
||
| return ( | ||
| <CssColorSchemeContext.Provider value={contextValue}> |
There was a problem hiding this comment.
Should we directly include here the ThemeScope if it is a nested theme?
CSSThemeProvider aims to do what
|
4b582d6 to
32e8322
Compare
0971d65 to
69c4173
Compare
There was a problem hiding this comment.
I don't like that we have a "CSS in JS" implementation for the breakpoints, and moreover that we need to use a different file and JS syntax for defining the styles for the breakpoints.
Maybe the main benefit of the other approach where we built the CSS and provide users the content would be that the breakpoints can be an input and we can use some kind of "replace" method on them. Ideally, I would like to be able to do something like:
@media (min-width: $xs) {
.header {/* some styles */}
}
and have the xs be replaced with the actual breakpoint. We are going here in the waters of zero-runtime CSS in JS, I think Pigment CSS already had something like this. cc @brijeshb42 may have more ideas on how we can solve this.
|
Maybe one option could be to export a version of our bundles, that import CSS modules, and that use edit: See |
|
|
||
| // Components register at module import time; batch those imports into one update. | ||
| // Name-keying dedupes repeated imports/HMR for the same component. | ||
| export function registerBreakpointStyles(name: string, descriptor: BreakpointStylesDescriptor) { |
There was a problem hiding this comment.
Have you tested this with server side rendering or RSC ?
|
There's no clear solution here in the absence of build-time extraction. Both @mnajdova and @Janpot's solution are on the same lines. Jan's is the standard one. In our css modules, we'll need to use |
|
Using a postcss plugin makes sense to me 👌 This is how mantine solved the problem - https://mantine.dev/styles/responsive/#breakpoints-variables-in-css-modules |
This is what attracts me to this solution, at least one widely used CSS pre-processor would work out-of-the box, no plugins required. |
This PR aims to act as a POC for using a CSSThemeProvider instead of ThemeProvider, which should not import anything Emotion related. To be used by consumers who want to replace Emotion styling with anything else: Tailwind, CSS Modules etc. It's one of the ways we can remove Emotion and keep the Theme generation at runtime. The other way is to generate the theme at build time and import the file. This PR will cover just the first option. The other PR is here.
Changes:
Summary
Component CSS
SliderandToolbarfromstyled()arguments to CSS Modules.styled()slot shells so Emotion users retainsx,styled(), variants, andstyleOverrides.:global(.Mui*)selectors to preserve MUI's stable public class names.@media (--mui-breakpoint-up-sm).CSS Delivery
Provide two mutually exclusive component CSS paths:
Default breakpoints:
buildCssArtifacts.mtscombines component CSS Modules into@mui/material/styles.css.@mui/material/styles.cssonce.Custom breakpoints:
buildCssModulesVariant.mtscreates a parallel@mui/material/css-modules/*package tree.muiMaterialCssModules()maps normal Material UI imports to the parallel package.@mui/material/styles.css, as that would duplicate styles.CSS Build Tools
@mui/material-css-toolsfor build-time integrations.muiMaterialCssModules()to select the parallel package in Vite.muiCustomMedia({ theme })adapters for Vite/Lightning CSS and PostCSS.generateBreakpointCustomMedia()andgetBreakpointCustomMediaDefinitions()for custom bundler integrations.@media (--mui-breakpoint-up-tablet).breakpoint values.
CssThemeProvider
CssThemeProvideras a runtime replacement forThemeProvideron the no-Emotion path.createTheme({ cssVariables: true }).CssVarsInjector.<style>element into the HTML stream.useInsertionEffectinserts or updates the style in<head>.@mui/private-theming, allowinguseTheme()and default props to work.
RtlProvider.sx,styled(), theme variants, andstyleOverridesdo not generate CSS when the noopstyling engine is active.
sxemits a development warning and is ignored.Color Scheme
useCssColorScheme()for trees managed byCssThemeProvider.useColorSchemeSetup, which is used by bothCssVarsProviderandCssThemeProvider.color-scheme attribute.
noSsrcurrently affects color-scheme initialization; it does not suppress server renderingor render a placeholder as its prop documentation suggests.
Theme Scoping
ThemeScopeas the DOM boundary for nested CSS-variable themes.getThemeScopeProps()derives the scope class and color-scheme attribute from simpleclass-based
rootSelectorvalues.ThemeProviderandCssThemeProviderautomatically render it for nested themes.ThemeScopeContextexposes the active scope to portal components.useThemeScopeProps()copies the scope onto a portal root.ScopedDialogdemonstrates the integration throughslotProps.root.Modalor a shared portal container.styleIdvalues sotheir injected styles do not overwrite one another.
No-Emotion Engine
@mui/styled-engine-noop.@mui/styled-engineto the noop package in bothCssThemeProvidersandboxes.Tailwind CSS
@mui/tailwindwith Tailwind v3 preset/plugin and Tailwind v4 CSS integration.@mui/material-css-tools:createTailwindPreset(theme)for Tailwind v3.generateTailwindThemeCss(theme)for Tailwind v4.@mui/tailwind/v4.csswithout requiringpostcss-import.Sandboxes
emotion-vite-sandbox: Emotion, runtimeThemeProvider, CSS Modules, PostCSS,custom
sm=720px, and Tailwind v3.css-theme-provider-vite-sandbox: noop engine, runtimeCssThemeProvider, CSS Modules,Lightning CSS, custom
sm=720px, and Tailwind v4.css-theme-provider-default-css-vite-sandbox: noop engine, runtimeCssThemeProvider,aggregate
@mui/material/styles.css, defaultsm=600px, and Tailwind v4.useTheme(),useCssColorScheme(), plain CSS overrides, ignoredsx, and Tailwind tokens.styleOverridesexample.buildMuiCss.mtsscripts generate Tailwind files only. Component CSS and breakpointprocessing are handled by the selected CSS delivery path.
Docs Experiments
/experiments/emotion-slider/and/experiments/css-slider/.the Emotion styling engine.
accurately test the migrated component base styles.
Verification