Skip to main content
Tailwind includes a dark variant that lets you style your site differently when dark mode is enabled.

Dark Mode Strategies

Tailwind supports multiple strategies for implementing dark mode. Choose the one that best fits your projectโ€™s needs.

Media Query Strategy (Default)

By default, Tailwind uses the prefers-color-scheme media query to detect dark mode:
<!-- Automatically uses system preference -->
<div class="bg-white dark:bg-gray-900">
  <h1 class="text-gray-900 dark:text-white">
    Hello World
  </h1>
</div>
This generates CSS like:
.bg-white {
  background-color: #fff;
}

@media (prefers-color-scheme: dark) {
  .dark\:bg-gray-900 {
    background-color: #111827;
  }
}
From the source code, the media strategy is implemented as:
if (mode === 'media') {
  addVariant('dark', '@media (prefers-color-scheme: dark)')
}

Selector Strategy (Class-based)

For manual dark mode toggling, use the selector strategy by adding a dark class to your HTML:
@theme {
  --dark-mode: selector;
}
Now dark mode is controlled by a .dark class on a parent element:
<!-- Light mode -->
<html>
  <body>
    <div class="bg-white dark:bg-gray-900">
      <!-- bg-white -->
    </div>
  </body>
</html>

<!-- Dark mode -->
<html class="dark">
  <body>
    <div class="bg-white dark:bg-gray-900">
      <!-- bg-gray-900 -->
    </div>
  </body>
</html>
The selector strategy uses the :where() pseudo-class for specificity control:
if (mode === 'selector') {
  addVariant('dark', `&:where(${selector}, ${selector} *)`)
}
This generates CSS like:
.bg-white {
  background-color: #fff;
}

.dark\:bg-gray-900:where(.dark, .dark *) {
  background-color: #111827;
}

Custom Selector

Customize the dark mode selector:
@theme {
  --dark-mode: selector(.nightmode);
}
<html class="nightmode">
  <!-- Dark mode styles apply -->
</html>

Variant Strategy

For complete control, use the variant strategy with a custom selector pattern:
@theme {
  --dark-mode: variant('[data-theme="dark"] &');
}
<html data-theme="dark">
  <body>
    <div class="bg-white dark:bg-gray-900">
      <!-- bg-gray-900 -->
    </div>
  </body>
</html>
When using the variant strategy, you must include & in your selector to indicate where the utilityโ€™s selector should be inserted.

Common Dark Mode Patterns

Color Schemes

<div class="bg-white dark:bg-gray-900">
  <h1 class="text-gray-900 dark:text-white">
    Heading
  </h1>
  <p class="text-gray-600 dark:text-gray-300">
    Body text
  </p>
</div>

Form Elements

<input
  type="text"
  class="
    bg-white dark:bg-gray-800
    border border-gray-300 dark:border-gray-600
    text-gray-900 dark:text-white
    placeholder-gray-400 dark:placeholder-gray-500
    focus:border-blue-500 dark:focus:border-blue-400
    focus:ring-blue-500 dark:focus:ring-blue-400
  "
  placeholder="Search..."
/>

Images & Icons

<!-- Swap images -->
<img class="block dark:hidden" src="logo-light.svg" alt="Logo" />
<img class="hidden dark:block" src="logo-dark.svg" alt="Logo" />

<!-- Adjust icon colors -->
<svg class="text-gray-600 dark:text-gray-400">
  <!-- Icon path -->
</svg>

Gradients

<div class="
  bg-gradient-to-r
  from-blue-500 to-purple-600
  dark:from-blue-600 dark:to-purple-700
">
  Gradient that adapts to dark mode
</div>

Combining with Other Variants

Dark mode works seamlessly with all other Tailwind variants:

Hover States

<button class="
  bg-blue-500 hover:bg-blue-600
  dark:bg-blue-600 dark:hover:bg-blue-700
  text-white
">
  Hover me
</button>

Focus States

<input class="
  border-gray-300 focus:border-blue-500 focus:ring-blue-500
  dark:border-gray-600 dark:focus:border-blue-400 dark:focus:ring-blue-400
" />

Responsive Design

<div class="
  bg-white md:bg-gray-50
  dark:bg-gray-900 dark:md:bg-gray-800
">
  Different colors per breakpoint and mode
</div>

Group Hover

<div class="group">
  <div class="
    bg-white group-hover:bg-gray-50
    dark:bg-gray-800 dark:group-hover:bg-gray-700
  ">
    Nested element responds to parent hover in both modes
  </div>
</div>

Toggling Dark Mode

When using the selector strategy, implement dark mode toggling with JavaScript:
// 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')

Avoiding Flash of Unstyled Content

When using the selector strategy, add this script to your <head> before any other content to prevent a flash of the wrong theme:
<script>
  // On page load
  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')
  }
</script>
This script must run synchronously in the <head> before your content renders. Donโ€™t defer or async it.

Best Practices

Design System Consistency

Define semantic color variables that work in both modes:
@theme {
  /* Light mode colors */
  --color-background: white;
  --color-foreground: black;
  --color-primary: #3b82f6;
  
  /* Dark mode overrides */
  @media (prefers-color-scheme: dark) {
    --color-background: #111827;
    --color-foreground: white;
    --color-primary: #60a5fa;
  }
}

Accessibility

Ensure sufficient contrast in both modes:
<!-- Good: High contrast in both modes -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">

<!-- Bad: Poor contrast in dark mode -->
<div class="bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400">

Test Both Modes

Always test your UI in both light and dark modes to ensure:
  • Sufficient color contrast
  • Readable text
  • Visible borders and dividers
  • Proper image handling
  • Correct icon colors
Use browser DevTools to toggle between light and dark mode preferences for quick testing.

Build docs developers (and LLMs) love