paypal / glamorous

DEPRECATED: 💄 Maintainable CSS with React
https://glamorous.rocks
MIT License
3.64k stars 208 forks source link

Changing themes dynamically with Glamorous #401

Closed nareshbhatia closed 6 years ago

nareshbhatia commented 6 years ago

I asked this question on the glamor project, but didn't get a response in 3 weeks. Would really appreciate if someone can point out the issue with my code or offer a better solution.

TL;DR: To change themes dynamically, I am calling css.global('body', {...} as follows:

    render() {
        const { paletteType } = rootStore.appStore;
        const palette = {...};
        const theme = createMuiTheme({ palette });

        css.global('body', {
            ...
            background: theme.palette.background.default,
            color: theme.palette.text.primary,
            ...
        });

        return (
            <ThemeProvider theme={theme}>
                <Provider rootStore={rootStore}>
                    <Shell />
                </Provider>
            </ThemeProvider>
        );
    }

This works when I toggle the theme twice, but gets stuck on the third time! What am I doing wrong? Is there a better way using Glamorous?

You can see the full working code here: https://github.com/nareshbhatia/mobx-shop-glamorous

kentcdodds commented 6 years ago

Hi @nareshbhatia!

If you could create an example of this following these instructions we could probably help you better: http://help.glamorous.rocks

nareshbhatia commented 6 years ago

Sure, here's an example: https://codesandbox.io/s/oql4ywwxz5. This one actually gets stuck after a single toggle. I threw in a console.log() to make sure that paletteType is indeed toggling every time the button is clicked.

tikotzky commented 6 years ago

This "issue" you are seeing is because of the way glamor manages adding styles to the page. There are 2 behaviors combining to cause the behavior you are experiencing.

  1. glamor never removes rules added to the page (it only adds new rules as it comes across them)
  2. if you add a rule which was already added once before glamor does not add any styles to the page (since the rule is already on the page due to the above behavior)

In your case when rendering initially it adds a rule of

body{background:#ffffff;color:#000000;}

later when you toggle the style it adds another rule causing the combined styles to look like

body{background:#ffffff;color:#000000;}
body{background:#303030;color:#ffffff;}

This causes the page to take on the styles of the second rule When you tell glamor to add the first rules again, it does a noop since the rule is already in the page. The reason why the styling doesn't change is because the second rule will end up staying on the page and always override the first.

Hope that explains the behavior a little :)

That being said using global with glamor is really an escape hatch that should only be used for css resets and other things that need to be global. In your case you'd be be able to get the behavior you're looking for by generating a classname with glamor and manually setting it on the body element while rendering.

    document.body.className = css({
      background: theme.background,
      color: theme.color
    });

here is a working sandbox: https://codesandbox.io/s/5vkml9x7rx

nareshbhatia commented 6 years ago

Great explanation, @tikotzky. Learned something new. And your workaround is perfect!

Thanks.