Skip to content

Tailwind CSS - Dark Mode

Dark mode has become an essential feature for modern websites and applications, reducing eye strain in low-light environments. Tailwind CSS provides a powerful and flexible system for implementing dark mode using a special dark variant.

Section titled “The Recommended Strategy: Class-based Toggling”

The most flexible and recommended way to manage dark mode is the class strategy. This method applies dark mode styles whenever a .dark class is present on a parent element, typically the <html> tag. This gives you full control to toggle dark mode manually with JavaScript.

First, you need to enable the class strategy in your configuration file.

/** @type {import('tailwindcss').Config} */
module.exports = {
// Set darkMode to 'class' to enable manual toggling
darkMode: 'class',
content: [
'./*.{html,js}',
// Add paths to all of your template files
],
theme: {
extend: {},
},
plugins: [],
}

Step 2: Style Elements with the dark: Variant

Section titled “Step 2: Style Elements with the dark: Variant”

Now, you can prefix any utility with dark: to specify a style that should only apply when dark mode is active. The first class is for light mode, and the dark:-prefixed class is for dark mode.

<!--
- bg-white will be used by default (light mode).
- dark:bg-slate-900 will be used when the .dark class is active.
-->
<body class="bg-white dark:bg-slate-900 text-slate-600 dark:text-slate-300">
<!--
- The card has a light background by default.
- In dark mode, it gets a darker background.
-->
<div class="bg-slate-100 dark:bg-slate-800 p-6 rounded-lg shadow-md">
<h2 class="text-slate-900 dark:text-white">Card Title</h2>
<p>This is some content inside the card.</p>
</div>
</body>

Here is a complete, runnable example that shows how to create a theme toggle button. This script respects the user’s OS preference, persists their choice in localStorage, and allows them to toggle the theme manually.

<!DOCTYPE html>
<!-- The 'class' attribute will be dynamically populated by JavaScript -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tailwind CSS Dark Mode Toggle</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
// Set darkMode to 'class' in your tailwind.config.js for this to work.
module.exports = { darkMode: 'class' }
</script>
</head>
<body class="bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-300 font-sans">
<div class="min-h-screen flex flex-col items-center justify-center p-4">
<div class="w-full max-w-md bg-gray-100 dark:bg-gray-800 rounded-xl shadow-lg p-8">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Dark Mode Example</h1>
<p class="mb-6">Click the button below to toggle the theme.</p>
<button id="theme-toggle" type="button" class="px-4 py-2 font-semibold text-white bg-blue-600 dark:bg-yellow-400 dark:text-gray-900 rounded-lg hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-yellow-300">
<span class="dark:hidden">Switch to Dark</span>
<span class="hidden dark:inline">Switch to Light</span>
</button>
</div>
<p class="mt-4 text-sm text-gray-500">Your choice will be remembered.</p>
</div>
<script>
const themeToggle = document.getElementById('theme-toggle');
const docElement = document.documentElement;
// Function to apply the theme
const applyTheme = (theme) => {
if (theme === 'dark') {
docElement.classList.add('dark');
} else {
docElement.classList.remove('dark');
}
localStorage.setItem('theme', theme);
};
// Check for saved theme in localStorage or user's OS preference
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const initialTheme = savedTheme ? savedTheme : (prefersDark ? 'dark' : 'light');
applyTheme(initialTheme);
// Event listener for the toggle button
themeToggle.addEventListener('click', () => {
const currentTheme = docElement.classList.contains('dark') ? 'dark' : 'light';
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
applyTheme(newTheme);
});
</script>
</body>
</html>

For advanced use cases, like integrating with a component library that uses a different class name or data attribute, you can customize the dark mode selector in tailwind.config.js:

// Example: Using a data attribute instead of a class
// Now, dark mode will activate when an element has `data-theme="dark"`
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['selector', '[data-theme="dark"]'],
// ...
}