pacocoursey / next-themes

Perfect Next.js dark mode in 2 lines of code. Support System preference and any other theme with no flashing
https://next-themes-example.vercel.app/
MIT License
4.94k stars 177 forks source link

Allow nested providers #254

Open shekharkhedekar opened 7 months ago

shekharkhedekar commented 7 months ago

I've got cases (stories in storybook) where I want to have multiple ThemeProviders e.g.

// Top level storybook theme
<ThemeProvider forcedTheme="light">

   // Individual Story
    <ThemeProvider forcedTheme="light">
        // LIGHT VERSION OF COMPONENT
    </ThemeProvider>
    <ThemeProvider forcedTheme="dark">
        // DARK VERSION OF COMPONENT
    </ThemeProvider>
</ThemeProvider>

This currently does not work because of this code.

Currently, I'm forking your package and removing those lines to allow for nested providers. I'm also adding a component to add the theme class on container of the provider if the forcedTheme is set so the children will get the correct styles.

Not sure if this contradicts your original design, but it would be useful to us.

shrekuu commented 6 months ago

Hi @shekharkhedekar , I am using next-themes with TailwindCSS. It seems I can simply achieve nested themes like this:

  1. I add some config in Tailwind config:
    
    import plugin from 'tailwindcss/plugin';

export default { darkMode: 'class', plugins: [
plugin(function ({ addUtilities }) { const utilities = { // light, dark color scheme '.light': { 'color-scheme': 'light', }, '.dark': { 'color-scheme': 'dark', }, }; addUtilities(utilities); }), ], };


Then I can simply use this `light` or `dark` in any element like this:

```tsx
'use client';

export default function Page() {
  return (
    <div className="border border-red-400 p-2">
      <h1>default theme</h1>
      <input type="text" />
      <div className="border border-red-400 p-2 light">
        <h1>light</h1>
        <input type="text" />
        <div className="border border-red-400 p-2 dark">
          <h1>dark</h1>
          <input type="text" />
          <div className="border border-red-400 p-2 light">
            <h1>light</h1>
            <input type="text" />
          </div>
        </div>
      </div>
    </div>
  );
}
image

When I toggle the theme to light in next-themes, the nested dark section stays dark. Cool! This is just what I wanted.

image

In my app, I am forcing it to use dark theme by default in my outermost layout.tsx file.

<NextThemesProvider defaultTheme="dark" attribute="class">
...
</NextThemesProvider>
shekharkhedekar commented 6 months ago

@shrekuu we we're doing that as well, but we also needed to reference resolvedTheme in the nested theme, which always referenced the top level theme and produced inaccurate results.

universse commented 3 weeks ago

I made a library that supports this use case https://github.com/universse/palettez. Here's a demo https://palettez-nextjs-demo.vercel.app/multi-store-with-server-persistence. Hope it helps.