Skip to content
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

[styles] Theme Provider is not working correctly when nested themes are used #15914

Closed
naman13924 opened this issue May 28, 2019 · 33 comments
Closed
Labels
docs Improvements or additions to the documentation package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. v4.x
Milestone

Comments

@naman13924
Copy link

naman13924 commented May 28, 2019

Earlier themes were working fine before the 4.0 release but now the nested themes are overriding the themes which are outside its scope
for example in your documentation example https://codesandbox.io/s/material-demo-hdy1n

replace the demo.js code with the attached code in demo.txt file
demo.txt

The theme of components outside the nested themes are getting overridden by the nested one.
I am facing this challenge in my whole project as I have many nested themes and the ui has changed unexpectedly due to this,
earlier it was working fine with v3.9

@eps1lon eps1lon added bug 🐛 Something doesn't work package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. labels May 28, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented May 28, 2019

@naman13924 You need a top-level theme provider: https://codesandbox.io/s/material-demo-29fxp.

@oliviertassinari oliviertassinari added docs Improvements or additions to the documentation priority: important This change can make a difference and removed bug 🐛 Something doesn't work labels May 28, 2019
@oliviertassinari oliviertassinari added this to the v4.1 milestone May 28, 2019
@eps1lon
Copy link
Member

eps1lon commented May 28, 2019

So basically multiple theme roots are not allowed on a page. There has to be a single provider root. Wouldn't that make multiple react trees impossible on a page?

@oliviertassinari
Copy link
Member

oliviertassinari commented May 28, 2019

@eps1lon I have demonstrated the simplest workaround in the codesandbox: one ThemeProvider at the top of the tree.
For the multiple React tree pattern, people have to use a different seed if they want to use different themes: https://material-ui.com/styles/api/#creategenerateclassname-options-class-name-generator.
Another workaround is to use the disableGlobal option.

@eps1lon
Copy link
Member

eps1lon commented May 28, 2019

@eps1lon I have demonstrated the simplest workaround in the codesandbox: one ThemeProvider at the top of the tree.

Pretty sure that's what I said

So basically multiple theme roots are not allowed on a page. There has to be a single provider root.

For the multiple React tree pattern, people have to use a different seed if they want to use different themes

I would guess this is a pretty basic use case when using multiple react roots. That shouldn't be hidden behind nested API docs.

@oliviertassinari
Copy link
Member

@eps1lon Yes, I have repeated it for people that might have the problem and that skip read. It's a really important point. I do agree that there is close to no documentation it about and that we should address the problem. I wasn't sure it would impact many people. I suspect few people have this use case, but I don't have enough data to defend this claim. I will work on it as more people report it (hence gain priority over the other issues)

@oliviertassinari oliviertassinari self-assigned this May 28, 2019
@Ajaay
Copy link

Ajaay commented May 29, 2019

Am I right in thinking that this is what has broken support of our v0.X theme nesting?

@ztoben
Copy link

ztoben commented May 29, 2019

Also wondering that @Ajaay. We have a v0 theme nested in a now v4 theme and are running into a ton of issues that seem to be theme related.

@oliviertassinari
Copy link
Member

oliviertassinari commented May 29, 2019

@ztoben It's still supported (but not encouraged). Do you have a minimale reproduction?

@Ajaay
Copy link

Ajaay commented May 29, 2019 via email

@ztoben
Copy link

ztoben commented May 29, 2019

@oliviertassinari, it's in a private repo that's pretty complex. We're working on getting a reproduction case set up.

@nareshbhatia
Copy link
Contributor

@oliviertassinari, @eps1lon - can you please clarify what you mean by "Multiple theme roots are not allowed on a page. There has to be a single provider root"? In my sandbox example, I have a single provider root in index.tsx and a nested provider in App.tsx. However the nested theme is not working. The intent is to achieve a dark theme in the left sidebar whereas the rest of the app should be light themed. What am I missing?

@eps1lon
Copy link
Member

eps1lon commented Sep 12, 2019

With root I mean a provider which has no parent provider:

<React.Fragment>
	<ThemeProvider /> /* root */
	<ThemeProvider /> /* root */
	<ThemeProvider /> /* root */
</React.Fragment>
<React.Fragment>
	<ThemeProvider> /* root */
		<ThemeProvider /> /* child */
		<ThemeProvider /> /* child */
	</ThemeProvider>
</React.Fragment>

Edit:
The issue with dark mode in nested themes is that something like a dark mode requires global styles. You should only have one theme changing the dark/light mode per <body />.

@oliviertassinari
Copy link
Member

The intent is to achieve a dark theme in the left sidebar whereas the rest of the app should be light themed.

@eps1lon is right, in order to achieve this, you need a light theme provider that wraps your whole tree, a dark theme provider that wraps the side nav and a <Typography component="div"> that applies the right color property (as we rely on color inheritance when possible).

@nareshbhatia
Copy link
Contributor

Thanks for your responses @eps1lon & @oliviertassinari. Now I am clear that in my example, I have one light theme provider at the root of the page and one dark theme provider as a child that wraps the sidebar - which is essentially a List. So far so good.

The issue is, as @eps1lon mentioned, the dark theme relies on global styles. The List component does not even attempt to paint its background dark. It relies on the body background which unfortunately happens to be white. Similarly the text in the list is black because body color is black and ButtonBase sets the color to inherit.

Given this approach, I don't think nested themes will automatically switch between light and dark themes. However, I came up with a simple trick based on @oliviertassinari's hint that seems to work. I have introduced a <Paper> instance right below the nested theme. I also changed palette.background.paper to #303030 to get the correct dark background. This seems to work - the List background is dark and the text color is white. Here's the result. Please let me know if you find any holes with this approach.

Thanks again for your help.

@oliviertassinari
Copy link
Member

Looks good to me. If your side nav stays as simple, I would try not to nest two themes.

@nareshbhatia
Copy link
Contributor

@oliviertassinari, the side nav will stay that simple. I was relying on <Paper> to pick up the right colors from the nested theme. How do you propose doing it without a nested theme? Perhaps a simple <div> that changes background-color and color? Will that do the trick? Or did you have something else in mind?

@oliviertassinari
Copy link
Member

You could apply the dark background color and color present in the theme. So yes, with a simple div.

@nareshbhatia
Copy link
Contributor

Just tried a simple div with dark background and white color - it does not work nicely. Here's an example. While the list text works because it uses color="inherit", the icons and toggle buttons don't because they pickup their color from the parent theme. Based on this, I think the simplest solution is to have a nested theme. Thoughts?

@oliviertassinari
Copy link
Member

Makes sense.

@alturnwall

This comment has been minimized.

@mbrookes

This comment has been minimized.

@alturnwall

This comment has been minimized.

@oliviertassinari

This comment has been minimized.

@pkhodaveissi

This comment has been minimized.

@oliviertassinari

This comment has been minimized.

@pkhodaveissi
Copy link

@pkhodaveissi I think that your concern is different and outside of the scope of this issue. We didn't optimize for micro-frontend, nor do we aim to, but are still interested in finding improvement opportunities.

Sure I understand, but guess my question boils down to a solution to reduce the number of <style> tags introduced to the document while you're being forced to have multiple siblings of ThemeProvider element around(cuz of the architecture).

@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 12, 2020

An update, this issue is being resolved in v5 thanks to #22342. So far, we have migrated the Slider in the lab, where this can be tested. It no longer generates global class names, class names are hashed. However, the components have global class names that can be targeted.

@oliviertassinari oliviertassinari changed the title Theme Provider is not working correctly when nested themes are used [styles] Theme Provider is not working correctly when nested themes are used Nov 12, 2020
@oliviertassinari oliviertassinari added this to the v5 milestone Nov 12, 2020
@Zache
Copy link

Zache commented Feb 16, 2021

I'm affected by this issue, we have multiple react roots on the page and because of this the classnames that are generated aren't deterministic, causing components to re-render unnecessarily.

@ahayes91
Copy link

ahayes91 commented Mar 23, 2021

@eps1lon I have demonstrated the simplest workaround in the codesandbox: one ThemeProvider at the top of the tree.
For the multiple React tree pattern, people have to use a different seed if they want to use different themes: https://material-ui.com/styles/api/#creategenerateclassname-options-class-name-generator.
Another workaround is to use the disableGlobal option.

Ditto on this issue - I'm working on a single-spa app as @pkhodaveissi mentioned, and having multiple React apps on the page that each use a MuiThemeProvider component with the same theme in their tree are causing conflicting stylesheets and unexpected behaviour in our components.

(Note: This is only happening on pages with separately webpack-aged apps, if you know what I mean.)

I confirmed that upgrading to MUI 5 would indeed resolve the issue for us, but seeing as MUI 5 isn't ready yet and we have quite a lot of migration tasks to do from 4 -> 5, I'm looking for an interim solution in the meantime.
@oliviertassinari what solution would you recommend in this case, something like the following in each app on the page?

const generateClassName = createGenerateClassName({
  disableGlobal: true, // or seed: 'something_unique' ?
});
...
const App = () => {
...
return (
    <StylesProvider generateClassName={generateClassName}>
        <MuiThemeProvider theme={theme}>
        ....
        </MuiThemeProvider>
    </StylesProvider>
);
};

I couldn't quite figure out if StylesProvider is meant to replace or supplement MuiThemeProvider from the documentation.
Thanks!

@oliviertassinari
Copy link
Member

oliviertassinari commented Mar 23, 2021

@ahayes91 If you have multiple react roots, disableGlobal isn't enough. You need to have a unique seed for each class name generators.

I'm closing as this is fixed in v5.

@Johnrobmiller
Copy link

To be brutally honest, details about the ThemeProvider's technology should not even matter here, not even a little bit, or at least from the user's point of view. Concerns about a feature's design are almost always of greater significance than the technology that enables said design. And with this said, regarding the fact that multiple ThemeProviders must have a parent ThemeProvider element, there is an unasked yet obvious question that warrants asking: where is the user expected to learn about this somewhat esoteric yet potentially code-breaking ThemeProvider fact? In the issues section of the mui Github? No. tldr: this should not be a design feature to begin with, but since it is and hasn't been changed yet, the docs should be extremely clear about it, which they aren't.

@oliviertassinari
Copy link
Member

oliviertassinari commented Apr 11, 2021

@Johnrobmiller It's a v4 only issue, a version we don't actively support anymore.

This issue is gone in v5. We gave up on styling with global class names. This structural change harms the DX of customizations but yields other interesting features that make it a significant net positive.

@sag1v
Copy link

sag1v commented May 8, 2023

@oliviertassinari Sorry to bring this up again but we are in a middle of a migration from v4 to v5 and i would like to minimize the amount of research and surprises i'm going to face.
the seed solution for nested Themes in v4 wasn't 100% complete. The issue we had with this is that the seed wasn't chained to "nested MUI classes"

For example, given this style:

root: {
  '& .MuiBadge-badge': {
    // something
  }
}

The seed won't be chained to the MuiBadge-badge class name, therefore this selector will impact badges that are not part of this seed (scope) instead of targeting the actual badge we want.
We had to create a wrapper over makeStyles that goes through all keys (recursively) and chain the seed to all MUI-* classes.

The reason i'm bringing this is up, is to ask if the solution that was introduced in v5 bypass or address this issue in any way?
Otherwise, we will need to either fix this or at least have a way to workaround it in user-land (as we managed to do in v4).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to the documentation package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. v4.x
Projects
None yet
Development

No branches or pull requests