Tailwind CSS Dark Mode & Theme Configuration: dark Prefix and @theme Directive
Dark Mode (dark:)
Tailwind uses the dark: prefix to define styles for dark mode, supporting both media and class strategies.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class'
}
</script>
</head>
<body class="min-h-screen bg-white dark:bg-gray-900 transition-colors">
<div class="max-w-4xl mx-auto p-8 space-y-8">
<!-- Dark mode toggle button -->
<div class="flex justify-end">
<button onclick="document.documentElement.classList.toggle('dark')"
class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200">
Toggle Dark Mode
</button>
</div>
<!-- Text colors -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<h3 class="text-gray-900 dark:text-white font-semibold text-lg mb-4">Text Colors</h3>
<p class="text-gray-600 dark:text-gray-300">
This text appears dark gray in light mode and light gray in dark mode.
</p>
<p class="text-blue-600 dark:text-blue-400 mt-2">
Link colors also adapt based on the mode.
</p>
</div>
<!-- Card components -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg dark:shadow-gray-700/50 p-6">
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center mb-4">
<span class="text-blue-600 dark:text-blue-400 text-xl">🎨</span>
</div>
<h4 class="text-gray-900 dark:text-white font-semibold mb-2">Theme Adaptation</h4>
<p class="text-gray-600 dark:text-gray-400">Card backgrounds, shadows, and text colors all adapt to dark mode.</p>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg dark:shadow-gray-700/50 p-6">
<div class="w-12 h-12 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center mb-4">
<span class="text-green-600 dark:text-green-400 text-xl">✨</span>
</div>
<h4 class="text-gray-900 dark:text-white font-semibold mb-2">Smooth Transitions</h4>
<p class="text-gray-600 dark:text-gray-400">Combined with transition-colors for smooth color switching.</p>
</div>
</div>
<!-- Form elements -->
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<h3 class="text-gray-900 dark:text-white font-semibold mb-4">Form Elements</h3>
<div class="space-y-4">
<input type="text" placeholder="Input field"
class="w-full p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400">
<button class="w-full p-3 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 transition-colors">
Submit Button
</button>
</div>
</div>
</div>
</body>
</html>
Traditional CSS vs Tailwind The traditional approach requires writing
@media (prefers-color-scheme: dark)or manually adding.darkclasses with numerous CSS rules, whereas Tailwind uses prefixes likedark:bg-gray-900 dark:text-whitefor inline declarations.
darkMode: 'class' strategy requires toggling the dark class on the <html> element. The darkMode: 'media' strategy automatically follows the system preference.
Custom Theme (tailwind.config)
You can extend or override the default theme through the tailwind.config file, adding custom colors, fonts, spacing, and more.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Theme Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
accent: '#f59e0b',
brand: '#8b5cf6',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
display: ['Poppins', 'sans-serif'],
},
spacing: {
'128': '32rem',
'144': '36rem',
},
borderRadius: {
'4xl': '2rem',
},
},
},
}
</script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
<div class="max-w-4xl mx-auto space-y-8">
<!-- Custom colors -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Custom Colors</h3>
<div class="flex flex-wrap gap-4">
<div class="w-20 h-20 bg-primary-500 rounded-lg flex items-center justify-center text-white text-xs">primary-500</div>
<div class="w-20 h-20 bg-primary-600 rounded-lg flex items-center justify-center text-white text-xs">primary-600</div>
<div class="w-20 h-20 bg-primary-700 rounded-lg flex items-center justify-center text-white text-xs">primary-700</div>
<div class="w-20 h-20 bg-accent rounded-lg flex items-center justify-center text-white text-xs">accent</div>
<div class="w-20 h-20 bg-brand rounded-lg flex items-center justify-center text-white text-xs">brand</div>
</div>
</div>
<!-- Custom fonts -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Custom Fonts</h3>
<p class="font-sans text-lg mb-2">font-sans: Using Inter font</p>
<p class="font-display text-lg">font-display: Using Poppins font</p>
</div>
<!-- Custom spacing -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Custom Spacing</h3>
<div class="space-y-2">
<div class="h-4 bg-primary-200 rounded" style="width: 32rem;">
<span class="text-xs">w-128 (32rem)</span>
</div>
<div class="h-4 bg-primary-300 rounded" style="width: 36rem;">
<span class="text-xs">w-144 (36rem)</span>
</div>
</div>
</div>
<!-- Custom border radius -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Custom Border Radius</h3>
<div class="flex gap-4">
<div class="w-24 h-24 bg-brand rounded-4xl flex items-center justify-center text-white text-xs">rounded-4xl</div>
<div class="w-24 h-24 bg-accent rounded-full flex items-center justify-center text-white text-xs">rounded-full</div>
</div>
</div>
</div>
</body>
</html>
Traditional CSS vs Tailwind The traditional approach requires defining numerous variables and classes in CSS, whereas Tailwind extends the theme through the
extendoption intailwind.config, automatically generating corresponding utility classes.
@theme Directive (v4 New Feature)
Tailwind v4 introduces the @theme directive, using CSS-native syntax to define theme variables without needing a JavaScript configuration file.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@theme Directive Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<style type="text/tailwindcss">
@theme {
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-200: #bfdbfe;
--color-primary-300: #93c5fd;
--color-primary-400: #60a5fa;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
--color-primary-800: #1e40af;
--color-primary-900: #1e3a8a;
--color-accent: #f59e0b;
--color-brand: #8b5cf6;
--font-sans: 'Inter', system-ui, sans-serif;
--font-display: 'Poppins', sans-serif;
--spacing-128: 32rem;
--spacing-144: 36rem;
--radius-4xl: 2rem;
}
</style>
</head>
<body class="min-h-screen bg-gray-100 p-8">
<div class="max-w-4xl mx-auto space-y-8">
<!-- Colors defined with @theme -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Colors Defined with @theme</h3>
<div class="flex flex-wrap gap-4">
<div class="w-20 h-20 bg-primary-500 rounded-lg flex items-center justify-center text-white text-xs">primary-500</div>
<div class="w-20 h-20 bg-primary-600 rounded-lg flex items-center justify-center text-white text-xs">primary-600</div>
<div class="w-20 h-20 bg-accent rounded-lg flex items-center justify-center text-white text-xs">accent</div>
<div class="w-20 h-20 bg-brand rounded-lg flex items-center justify-center text-white text-xs">brand</div>
</div>
</div>
<!-- Fonts defined with @theme -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Fonts Defined with @theme</h3>
<p class="font-sans text-lg mb-2">font-sans: Using Inter font</p>
<p class="font-display text-lg">font-display: Using Poppins font</p>
</div>
<!-- Spacing defined with @theme -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Spacing Defined with @theme</h3>
<div class="space-y-2">
<div class="h-4 bg-primary-200 rounded w-128">
<span class="text-xs">w-128 (32rem)</span>
</div>
<div class="h-4 bg-primary-300 rounded w-144">
<span class="text-xs">w-144 (36rem)</span>
</div>
</div>
</div>
<!-- Border radius defined with @theme -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Border Radius Defined with @theme</h3>
<div class="flex gap-4">
<div class="w-24 h-24 bg-brand rounded-4xl flex items-center justify-center text-white text-xs">rounded-4xl</div>
</div>
</div>
</div>
</body>
</html>
Traditional CSS vs Tailwind The traditional approach requires defining themes using JavaScript objects in
tailwind.config.js, whereas Tailwind v4's@themedirective uses CSS-native variable syntax, which is more intuitive and compatible with native CSS variables.
@theme directive follow naming conventions like --color-*, --font-*, --spacing-*, --radius-*, etc. Tailwind automatically generates corresponding utility classes.
CSS Native Variables and Tailwind Integration
Tailwind v4 supports using CSS native variables directly, without needing to go through a configuration file.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Native Variable Integration</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
:root {
--main-bg: #f8fafc;
--card-bg: #ffffff;
--text-primary: #1e293b;
--text-secondary: #64748b;
--border-color: #e2e8f0;
--spacing-section: 2rem;
}
.dark {
--main-bg: #0f172a;
--card-bg: #1e293b;
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--border-color: #334155;
}
</style>
</head>
<body class="min-h-screen p-8" style="background-color: var(--main-bg);">
<div class="max-w-4xl mx-auto space-y-8" style="gap: var(--spacing-section);">
<!-- Dark mode toggle -->
<div class="flex justify-end">
<button onclick="document.documentElement.classList.toggle('dark')"
class="p-2 rounded-lg" style="background-color: var(--card-bg); color: var(--text-primary);">
Toggle Dark Mode
</button>
</div>
<!-- Card using CSS variables -->
<div class="rounded-lg shadow p-6" style="background-color: var(--card-bg); border: 1px solid var(--border-color);">
<h3 class="font-semibold text-lg mb-4" style="color: var(--text-primary);">CSS Native Variables</h3>
<p style="color: var(--text-secondary);">
This card uses CSS native variables to define colors, applied via the style attribute.
</p>
</div>
<!-- Mixed usage -->
<div class="rounded-lg shadow p-6 bg-white dark:bg-gray-800">
<h3 class="font-semibold text-lg mb-4 text-gray-900 dark:text-white">Mixed Usage</h3>
<p class="text-gray-600 dark:text-gray-300 mb-4">
You can use both Tailwind classes and CSS variables together.
</p>
<div class="flex gap-4">
<div class="w-16 h-16 rounded-lg" style="background-color: var(--text-primary);"></div>
<div class="w-16 h-16 rounded-lg bg-blue-500"></div>
<div class="w-16 h-16 rounded-lg" style="background-color: var(--border-color);"></div>
</div>
</div>
</div>
</body>
</html>
Plugin System Introduction
Tailwind plugins can extend the framework's functionality by adding custom utility classes, variants, or base styles.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plugin System Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
const plugin = tailwind.plugin;
// Custom plugin: add text gradient utility classes
const textGradientPlugin = plugin(function({ addUtilities }) {
addUtilities({
'.text-gradient-blue': {
'background': 'linear-gradient(to right, #3b82f6, #8b5cf6)',
'-webkit-background-clip': 'text',
'-webkit-text-fill-color': 'transparent',
'background-clip': 'text',
},
'.text-gradient-red': {
'background': 'linear-gradient(to right, #ef4444, #f97316)',
'-webkit-background-clip': 'text',
'-webkit-text-fill-color': 'transparent',
'background-clip': 'text',
},
});
});
tailwind.config = {
plugins: [textGradientPlugin],
}
</script>
</head>
<body class="min-h-screen bg-gray-100 p-8">
<div class="max-w-4xl mx-auto space-y-8">
<!-- Utility classes added by the plugin -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Custom Plugin: Text Gradient</h3>
<p class="text-4xl font-bold text-gradient-blue mb-4">Blue gradient text</p>
<p class="text-4xl font-bold text-gradient-red">Red gradient text</p>
</div>
<!-- Common third-party plugin examples -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="font-semibold mb-4">Common Plugins</h3>
<div class="space-y-4">
<div class="p-4 bg-gray-50 rounded-lg">
<h4 class="font-medium text-gray-900">@tailwindcss/typography</h4>
<p class="text-gray-600 text-sm">Provides beautiful typographic styles for long-form content using the prose class.</p>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<h4 class="font-medium text-gray-900">@tailwindcss/forms</h4>
<p class="text-gray-600 text-sm">Provides base style resets for form elements, ensuring consistent behavior across browsers.</p>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<h4 class="font-medium text-gray-900">@tailwindcss/aspect-ratio</h4>
<p class="text-gray-600 text-sm">Provides aspect-ratio utility classes for controlling element width-to-height ratios.</p>
</div>
</div>
</div>
</div>
</body>
</html>
Traditional CSS vs Tailwind The traditional approach requires manually writing CSS classes and importing them, whereas Tailwind plugins automatically integrate into the build process through APIs like
addUtilities,addComponents, andaddBase.
@theme directive and @layer can replace most plugin use cases.
❓ FAQ
'media' strategy automatically follows the system's prefers-color-scheme media query, and users cannot manually toggle it. The 'class' strategy requires manually toggling the dark class on the <html> element, which is suitable for scenarios where a toggle button is provided.tailwind.config defines themes using JavaScript objects and requires a configuration file. The @theme directive uses CSS-native variable syntax, defined directly in CSS. It is a new feature in Tailwind v4 and is the recommended approach.dark: prefix with hidden and block. For example, <img class="block dark:hidden" src="light.png"> and <img class="hidden dark:block" src="dark.png">.style="color: var(--text-primary)".📖 Summary
- The
dark:prefix defines styles for dark mode, supporting bothmediaandclassstrategies - The
theme.extendoption intailwind.configcan extend the default theme - The
@themedirective is a new Tailwind v4 feature that uses CSS-native variable syntax to define themes - CSS native variables can be mixed with Tailwind classes via the
styleattribute - The plugin system extends functionality through APIs like
addUtilitiesandaddComponents - Dark mode combined with
transition-colorsenables smooth color transitions
📝 Exercises
-
⭐ Create a login page that supports dark mode, including input fields, buttons, and cards, using the
dark:prefix to adapt all elements -
⭐⭐ Use the
@themedirective to customize a brand theme (including primary color, secondary color, and fonts), and create a showcase page -
⭐⭐⭐ Create a theme switcher that supports light/dark/system modes, using CSS variables for dynamic theme color switching



