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
5.07k stars 181 forks source link

Set color-scheme: dark with Tailwind and custom themes #221

Open sspenst opened 1 year ago

sspenst commented 1 year ago

I have a bunch of themes on my site and I'd like to get it so I can use dark: Tailwind classes with specific themes. In the snippet below I would like theme-modern to be a dark mode theme:

<ThemeProvider themes={['theme-modern', 'theme-light', 'theme-dark']} attribute='class'>

I saw your post here so I've been trying the following with no luck yet:

[data-theme="theme-modern"] {
  color-scheme: dark;
}

Wondering if Tailwind is supported for this use case or if I'm missing a step somewhere. Thanks!

CSpencerND commented 10 months ago

@sspenst I made a theme called dim and added the following to my global css:

:is(.dim .dark\:prose-invert) {
    --tw-prose-body: var(--tw-prose-invert-body);
    --tw-prose-headings: var(--tw-prose-invert-headings);
    --tw-prose-lead: var(--tw-prose-invert-lead);
    --tw-prose-links: var(--tw-prose-invert-links);
    --tw-prose-bold: var(--tw-prose-invert-bold);
    --tw-prose-counters: var(--tw-prose-invert-counters);
    --tw-prose-bullets: var(--tw-prose-invert-bullets);
    --tw-prose-hr: var(--tw-prose-invert-hr);
    --tw-prose-quotes: var(--tw-prose-invert-quotes);
    --tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);
    --tw-prose-captions: var(--tw-prose-invert-captions);
    --tw-prose-kbd: var(--tw-prose-invert-kbd);
    --tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);
    --tw-prose-code: var(--tw-prose-invert-code);
    --tw-prose-pre-code: var(--tw-prose-invert-pre-code);
    --tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);
    --tw-prose-th-borders: var(--tw-prose-invert-th-borders);
    --tw-prose-td-borders: var(--tw-prose-invert-td-borders);
}

I don't know if that's what you're looking for but it did the job for my use-case.

sspenst commented 10 months ago

@CSpencerND your comment gave me some inspiration to try this again and I finally got it working. The difficulty for my case is that I want two classes:

next-themes allows mapping custom themes to different class names, but in my case I need to map each class to multiple class names. I think my implementation could be a lot simpler if this was a feature:

<ThemeProvider
  attribute='class'
  themes={Object.values(Theme)}
  value={{
    [Theme.Modern]: ['dark', 'theme-modern'],
    [Theme.Light]: ['light', 'theme-light'],
    ...
  }}
>

What I ended up doing was creating a new attribute data-theme-dark that I keep track of manually based on the the current theme, and then updated my Tailwind config to have:

  darkMode: [
    'class',
    '[data-theme-dark="true"]',
  ],

I then used the idea from https://github.com/pacocoursey/next-themes/issues/77#issuecomment-1005071511 to update the color-scheme:

[data-theme-dark="true"] {
  color-scheme: dark;
}

Finally, for dark mode to work instantly I had to add the following script:

<script dangerouslySetInnerHTML={{
  __html: `
!function() {
  const theme = localStorage.getItem('theme');

  // set data-theme-dark for Tailwind dark classes
  document.documentElement.setAttribute('data-theme-dark', theme === 'theme-light' ? 'false' : 'true');

  // check for an invalid theme and default to theme-modern
  // ThemeProvider doesn't handle this case with defaultTheme so we have to do it manually here
  if (!${JSON.stringify(Object.values(Theme))}.includes(theme)) {
    localStorage.setItem('theme', 'theme-modern');
  }
}();
  `,
}} />

It would be great if there was a simple way to do this with next-themes, but for now this seems to get the job done.

ahkhanjani commented 2 weeks ago

I saw your post here so I've been trying the following with no luck yet:

[data-theme="theme-modern"] {
  color-scheme: dark;
}

Wondering if Tailwind is supported for this use case or if I'm missing a step somewhere. Thanks!

Hi. Here's something more generic for this part if anyone sees this:

html[data-theme$="dark"] { // $= to indicate that the theme name ends with "dark"
  color-scheme: dark;
}

html[data-theme^="dark"] // ^= for starts with
html[data-theme*="dark"] // *= for contains

More on here.

Basically any dark theme.