-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[pigment-css] Add stringifyTheme
for Pigment CSS integration
#42476
Conversation
Netlify deploy previewhttps://deploy-preview-42476--material-ui.netlify.app/ @material-ui/core: parsed: +0.13% , gzip: +0.17% Bundle size reportDetails of bundle changes (Toolpad) |
…t/runtime-theme
import { stringifyTheme } from './stringifyTheme'; | ||
import extendTheme from './extendTheme'; | ||
|
||
describe('StringifyTheme', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
describe('StringifyTheme', () => { | |
describe('stringifyTheme', () => { |
function serializeTheme(object: Record<string, any>) { | ||
const array = Object.entries(object); | ||
// eslint-disable-next-line no-plusplus | ||
for (let index = 0; index < array.length; index++) { | ||
const [key, value] = array[index]; | ||
if (!isSerializable(value) || key.startsWith('unstable_')) { | ||
delete object[key]; | ||
} else if (isPlainObject(value)) { | ||
object[key] = { ...value }; | ||
serializeTheme(object[key]); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to remove all the CSS variables values: colors, typography, etc., so it breaks at runtime if developers try to access them. For example theme.color.primary.main
would be wrong as soon as the theme changes. So if it's impossible to access, we would avoid a footgun.
Then for these dynamic values (have CSS variables), solve it with: https://github.com/mui/pigment-css/issues/129.
But honestly, the theme
replacement at build-time feels wrong in the first place. I don't think we should invest more time into it. From what I understand, we are only doing it for RSC, but it feels like the worst technical solution available if there is a working React.cache() alternative: either like yak is doing it, or like this: emotion-js/emotion#2978 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But how would you make it work for components that are using useTheme()
at runtime, for example, Material UI Tooltip?
At this point, I think we need it (can be an internal API) otherwise v6 will be delayed for at least a month.
From what I understand, we are only doing it for RSC
No, we are doing this because some components are accessing the theme at runtime (those that call useTheme()
.
but it feels like the worst technical solution available if there is a working React.cache() alternative
Again, I think I mentioned in some issue to you that React.cache
is still experimental. The options we have are:
- Use wyw-in-js to provide runtime theme (small effort)
- Go with your proposal, could be small or large effort because we don't know the edge cases and
React.cache
is still experimental.
To me, both options give almost the same result, (1) is a bit better because there is no useContext
involved compare to option (2). That's why I would go with option (1) and keep the API internal to launch v6 and then when the time is right, switch to option (2) if necessary.
What's your opinion? cc @mnajdova @brijeshb42
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React.cache is still experimental
https://nextjs.org/docs/app/building-your-application/caching#react-cache-function doesn't make it so clear.
But how would you make it work for components that are using useTheme() at runtime, for example, Material UI Tooltip?
The way I see it:
For the server bundle, you have a full duplicate of the theme object, you can import the same one that the plugin uses. This theme is made available with the <CssVarProvider>
which injects the theme for the children's server components that were not extracted at build time. The theme is propagated ideally with the React.cache()
API because it has no bundler integration needs or a global window value (maybe with some capabilities to have multiple versions of Pigment CSS run in the same app)
Then, the children use useTheme()
, but this module must not be flagged as a client bundle, so we remove "use client" to have it as a shared moduie: https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md#conventions-for-serverclient-components. That would work like this: emotion-js/emotion#2978 (comment)
A theme replacement during the build could be an option but it sounds more work and depends more on the bundler.
For the client bundle, you have the whole theme available, you can import the same one that the plugin uses. We make it propagate in the application with the React Context API, from <CssVarProvider>
who injects it with a child. I mean, the CssVarProvider stays a server component but uses a client component inside himself using https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#supported-pattern-passing-server-components-to-client-components-as-props. The challenge there is that for this to work, the server effectively needs to serialize the theme https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#passing-props-from-server-to-client-components-serialization, which breaks all its functions.
One way to solve this is to use Yak a plugin that I assume will somehow make this theme available in the client component (see how they use client hints https://yak.js.org/features#theming to create the theme).
Another, I don't know if it works, is to have both a client and a server component imports that theme, and inject it, so it's never serialized like in https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#passing-props-from-server-to-client-components-serialization.
Personally, I see no need to serialize/stringify a theme with this model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @DiegoAndai
…t/runtime-theme
b1574d0
to
e3790cc
Compare
@brijeshb42 the build passes, I believe that it's ready for the final review. |
@@ -9,7 +9,7 @@ | |||
"clean": "rimraf .next" | |||
}, | |||
"dependencies": { | |||
"@pigment-css/react": "^0.0.12", | |||
"@pigment-css/react": "^0.0.13", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latest for @pigment/react
is 0.0.13
. Only @pigment-css/nextjs-plugin
that's get updated in 0.0.14
.
Is that what you mean?
const theme = ${JSON.stringify(serializableTheme, null, 2)}; | ||
|
||
theme.breakpoints = createBreakpoints(theme.breakpoints || {}); | ||
theme.transitions = createTransitions(theme.transitions || {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also found that we'll have to have theme.shadows
for Paper
. But I see that it uses shadows
in non production env. Have you tried running the demo apps in dev mode and see if it works ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it works. The theme.shadows are in serializableTheme
in line 56.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Great work on this.
|
Sorry, that's a mistake. It should not appear in the docs, I am fixing it. |
blocked by mui/pigment-css#124closes mui/pigment-css#130
For mui/pigment-css#105
To make the serializable theme available at runtime because some components are accessing runtime theme with
useTheme
.Usage with Pigment CSS: