Basic usage

Now that dark mode is a first-class feature of many operating systems, it’s becoming more and more common to design a dark version of your website to go along with the default design.

To make this as easy as possible, Tailwind includes a dark variant that lets you style your site differently when dark mode is enabled:

Light mode

Writes Upside-Down

The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space.

Dark mode

Writes Upside-Down

The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space.

<div class="bg-white dark:bg-slate-800 rounded-lg px-6 py-8 ring-1 ring-slate-900/5 shadow-xl">
  <div>
    <span class="inline-flex items-center justify-center p-2 bg-indigo-500 rounded-md shadow-lg">
      <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><!-- ... --></svg>
    </span>
  </div>
  <h3 class="text-slate-900 dark:text-white mt-5 text-base font-medium tracking-tight">Writes Upside-Down</h3>
  <p class="text-slate-500 dark:text-slate-400 mt-2 text-sm">
    The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space.
  </p>
</div>

By default this uses the prefers-color-scheme CSS media feature, but you can also build sites that support toggling dark mode manually using the ‘selector’ strategy.


Toggling dark mode manually

If you want to support toggling dark mode manually instead of relying on the operating system preference, use the selector strategy instead of the media strategy:

The selector strategy replaced the class strategy in Tailwind CSS v3.4.1.

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'selector',
  // ...
}

Now instead of dark:{class} classes being applied based on prefers-color-scheme, they will be applied whenever the dark class is present earlier in the HTML tree.

<!-- Dark mode not enabled -->
<html>
<body>
  <!-- Will be white -->
  <div class="bg-white dark:bg-black">
    <!-- ... -->
  </div>
</body>
</html>

<!-- Dark mode enabled -->
<html class="dark">
<body>
  <!-- Will be black -->
  <div class="bg-white dark:bg-black">
    <!-- ... -->
  </div>
</body>
</html>

If you’ve set a prefix in your Tailwind config, be sure to add that to the dark class. For example, if you have a prefix of tw-, you’ll need to use the tw-dark class to enable dark mode.

How you add the dark class to the html element is up to you, but a common approach is to use a bit of JavaScript that reads a preference from somewhere (like localStorage) and updates the DOM accordingly.

Customizing the selector

Some frameworks (like NativeScript) have their own approach to enabling dark mode and add a different class name when dark mode is active.

You can customize the dark mode selector by setting darkMode to an array with your custom selector as the second item:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ['selector', '[data-mode="dark"]'],
  // ...
}

Tailwind will automatically wrap your custom dark mode selector with the :where() pseudo-class to make sure the specificity is the same as it would be when using the media strategy:

.dark\:underline:where([data-mode="dark"], [data-mode="dark"] *){
  text-decoration-line: underline
}

Supporting system preference and manual selection

The selector strategy can be used to support both the user’s system preference or a manually selected mode by using the window.matchMedia() API.

Here’s a simple example of how you can support light mode, dark mode, as well as respecting the operating system preference:

spaghetti.js
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
  document.documentElement.classList.add('dark')
} else {
  document.documentElement.classList.remove('dark')
}

// Whenever the user explicitly chooses light mode
localStorage.theme = 'light'

// Whenever the user explicitly chooses dark mode
localStorage.theme = 'dark'

// Whenever the user explicitly chooses to respect the OS preference
localStorage.removeItem('theme')

Again you can manage this however you like, even storing the preference server-side in a database and rendering the class on the server — it’s totally up to you.

Overriding the dark variant

If you’d like to replace Tailwind’s built-in dark variant with your own custom variant, you can do so using the variant dark mode strategy:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ['variant', '&:not(.light *)'],
  // ...
}

When using this strategy Tailwind will not modify the provided selector in any way, so be mindful of it’s specificity and consider using the :where() pseudo-class to ensure it has the same specificity as other utilities.

Using multiple selectors

If you have multiple scenarios where dark mode should be enabled, you can specify all of them by providing an array:

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ['variant', [
    '@media (prefers-color-scheme: dark) { &:not(.light *) }',
    '&:is(.dark *)',
  ]],
  // ...
}