Master dark mode implementation with Tailwind CSS. Learn both class-based and media-based strategies, create dark mode color schemes, build theme switchers, and implement best practices for accessible dark themes.
Dark mode has become an essential feature in modern web applications. It reduces eye strain, saves battery life on OLED screens, and provides users with choice and control over their experience.
Tailwind CSS makes dark mode implementation incredibly straightforward with the dark: variant prefix.
Why Dark Mode Matters:
Tailwind's Dark Mode Advantages:
class and mediadark: prefix for any utilityIn this lesson, you'll master dark mode implementation from configuration to production-ready theme switchers.
Tailwind supports two strategies for enabling dark mode. Choose based on your requirements.
1. Class Strategy (Recommended for Most Projects)
Enables dark mode when a dark class is present on <html> or <body>.
1// tailwind.config.js
2module.exports = {
3 darkMode: 'class',
4 // ... rest of config
5};Pros:
Cons:
Usage:
1<!-- Light mode by default -->
2<html>
3 <body>
4 <div class="bg-white dark:bg-gray-900">
5 Content
6 </div>
7 </body>
8</html>
9
10<!-- Enable dark mode by adding 'dark' class -->
11<html class="dark">
12 <body>
13 <div class="bg-white dark:bg-gray-900">
14 Content (now dark!)
15 </div>
16 </body>
17</html>2. Media Strategy (System Preference)
Enables dark mode based on user's OS/browser preference.
1// tailwind.config.js
2module.exports = {
3 darkMode: 'media',
4 // ... rest of config
5};Pros:
Cons:
Usage:
1<!-- Automatically dark when system prefers dark mode -->
2<div class="bg-white dark:bg-gray-900">
3 Content (dark if system is dark)
4</div>CSS Media Query Generated:
1@media (prefers-color-scheme: dark) {
2 .dark\:bg-gray-900 {
3 background-color: rgb(17 24 39);
4 }
5}Recommendation:
For most projects, use **darkMode: 'class'** with a theme switcher that:
The dark: variant works with any Tailwind utility. Simply prefix the utility with dark:.
Basic Usage:
1<!-- Background colors -->
2<div class="bg-white dark:bg-gray-900">
3 <!-- Light: white, Dark: gray-900 -->
4</div>
5
6<!-- Text colors -->
7<p class="text-gray-900 dark:text-white">
8 <!-- Light: dark text, Dark: white text -->
9</p>
10
11<!-- Border colors -->
12<div class="border border-gray-300 dark:border-gray-700">
13 <!-- Light: light border, Dark: dark border -->
14</div>Combined with Responsive Breakpoints:
1<!-- Mobile: pink, Tablet+: blue, Dark mode: purple -->
2<div class="bg-pink-500 md:bg-blue-500 dark:bg-purple-900">
3 Responsive + Dark mode!
4</div>
5
6<!-- Different sizes + dark mode -->
7<h1 class="text-2xl md:text-4xl dark:text-white">
8 Responsive heading with dark mode
9</h1>Combined with State Variants:
1<!-- Hover in light + dark mode -->
2<button class="
3 bg-blue-600 hover:bg-blue-700
4 dark:bg-blue-500 dark:hover:bg-blue-600
5 text-white
6">
7 Button with dark mode hover
8</button>
9
10<!-- Focus rings in both modes -->
11<input class="
12 border-gray-300 focus:border-blue-500 focus:ring-blue-500
13 dark:border-gray-600 dark:focus:border-blue-400 dark:focus:ring-blue-400
14">Complete Component Example:
1<div class="
2 bg-white dark:bg-gray-800
3 border border-gray-200 dark:border-gray-700
4 rounded-lg shadow-lg
5 p-6
6">
7 <h2 class="
8 text-2xl font-bold
9 text-gray-900 dark:text-white
10 mb-4
11 ">
12 Card Title
13 </h2>
14
15 <p class="
16 text-gray-600 dark:text-gray-300
17 mb-4
18 ">
19 Card content with perfect contrast in both modes.
20 </p>
21
22 <button class="
23 bg-blue-600 hover:bg-blue-700
24 dark:bg-blue-500 dark:hover:bg-blue-600
25 text-white
26 px-4 py-2 rounded
27 transition-colors
28 ">
29 Action
30 </button>
31</div>Dark Mode with Opacity:
1<!-- Semi-transparent backgrounds -->
2<div class="bg-white/90 dark:bg-gray-900/90 backdrop-blur">
3 Glassmorphism in both modes
4</div>
5
6<!-- Text with opacity -->
7<p class="text-gray-900/70 dark:text-white/70">
8 Subtle text in both modes
9</p>Creating effective dark mode requires thoughtful color choices. Here are best practices and common patterns.
1. Recommended Dark Mode Palette:
1// tailwind.config.js
2module.exports = {
3 theme: {
4 extend: {
5 colors: {
6 // Light mode backgrounds
7 'bg-primary': '#ffffff', // Main background
8 'bg-secondary': '#f9fafb', // Secondary background
9 'bg-tertiary': '#f3f4f6', // Tertiary background
10
11 // Dark mode backgrounds (use gray scale)
12 'dark-bg-primary': '#111827', // gray-900
13 'dark-bg-secondary': '#1f2937', // gray-800
14 'dark-bg-tertiary': '#374151', // gray-700
15
16 // Light mode text
17 'text-primary': '#111827', // gray-900
18 'text-secondary': '#6b7280', // gray-500
19 'text-tertiary': '#9ca3af', // gray-400
20
21 // Dark mode text
22 'dark-text-primary': '#f9fafb', // gray-50
23 'dark-text-secondary': '#d1d5db', // gray-300
24 'dark-text-tertiary': '#9ca3af', // gray-400
25
26 // Borders
27 'border-light': '#e5e7eb', // gray-200
28 'border-dark': '#374151', // gray-700
29 },
30 },
31 },
32};2. Semantic Color System:
1<!-- Light mode -->
2<div class="bg-bg-primary dark:bg-dark-bg-primary">
3 <h1 class="text-text-primary dark:text-dark-text-primary">Title</h1>
4 <p class="text-text-secondary dark:text-dark-text-secondary">Content</p>
5</div>3. Brand Colors in Dark Mode:
Brand colors often need adjustment in dark mode for proper contrast.
1// tailwind.config.js
2module.exports = {
3 theme: {
4 extend: {
5 colors: {
6 primary: {
7 // Light mode: darker shades
8 light: '#3b82f6', // blue-500
9 DEFAULT: '#2563eb', // blue-600
10
11 // Dark mode: lighter shades for contrast
12 dark: '#60a5fa', // blue-400
13 },
14 },
15 },
16 },
17};Usage:
1<button class="
2 bg-primary-DEFAULT dark:bg-primary-dark
3 text-white
4">
5 Brand button
6</button>4. Contrast Guidelines:
| Element Type | Light Mode | Dark Mode |
|--------------|-----------|-----------|
| Background | White to Light Gray | Dark Gray to Black |
| Text (Primary) | Near Black | Near White |
| Text (Secondary) | Medium Gray | Light Gray |
| Borders | Light Gray | Dark Gray |
| Shadows | Black/20% opacity | Black/40% opacity |
| Brand Colors | Darker shades | Lighter shades |
5. Elevation in Dark Mode:
Use lighter backgrounds for elevated elements:
1<!-- Cards appear elevated in dark mode -->
2<div class="
3 bg-white dark:bg-gray-800
4 shadow-lg dark:shadow-2xl
5">
6 Card content
7</div>
8
9<!-- Modals use even lighter backgrounds -->
10<div class="
11 bg-white dark:bg-gray-700
12 shadow-2xl
13">
14 Modal content
15</div>6. Images and Media:
1<!-- Reduce image brightness in dark mode -->
2<img
3 src="photo.jpg"
4 class="dark:brightness-75 dark:contrast-125"
5 alt="Photo"
6>
7
8<!-- Different images per mode -->
9<img
10 src="logo-light.png"
11 class="block dark:hidden"
12 alt="Logo"
13>
14<img
15 src="logo-dark.png"
16 class="hidden dark:block"
17 alt="Logo"
18>A complete theme switcher with system preference detection and localStorage persistence.
1. HTML Structure:
1<!-- Theme switcher button -->
2<button
3 id="theme-toggle"
4 class="
5 p-2 rounded-lg
6 bg-gray-200 dark:bg-gray-700
7 text-gray-800 dark:text-gray-200
8 hover:bg-gray-300 dark:hover:bg-gray-600
9 transition-colors
10 "
11 aria-label="Toggle theme"
12>
13 <!-- Sun icon (visible in dark mode) -->
14 <svg
15 id="theme-toggle-light-icon"
16 class="hidden dark:block w-5 h-5"
17 fill="currentColor"
18 viewBox="0 0 20 20"
19 >
20 <path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" />
21 </svg>
22
23 <!-- Moon icon (visible in light mode) -->
24 <svg
25 id="theme-toggle-dark-icon"
26 class="block dark:hidden w-5 h-5"
27 fill="currentColor"
28 viewBox="0 0 20 20"
29 >
30 <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
31 </svg>
32</button>2. JavaScript Implementation:
1// Theme switcher with localStorage and system preference
2(function() {
3 // Get theme from localStorage or system preference
4 const getTheme = () => {
5 if (localStorage.theme === 'dark') return 'dark';
6 if (localStorage.theme === 'light') return 'light';
7
8 // Check system preference
9 if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
10 return 'dark';
11 }
12
13 return 'light';
14 };
15
16 // Apply theme
17 const applyTheme = (theme) => {
18 if (theme === 'dark') {
19 document.documentElement.classList.add('dark');
20 } else {
21 document.documentElement.classList.remove('dark');
22 }
23 };
24
25 // Set theme and persist
26 const setTheme = (theme) => {
27 localStorage.theme = theme;
28 applyTheme(theme);
29 };
30
31 // Initialize theme on page load
32 applyTheme(getTheme());
33
34 // Theme toggle button handler
35 document.addEventListener('DOMContentLoaded', () => {
36 const themeToggle = document.getElementById('theme-toggle');
37
38 if (themeToggle) {
39 themeToggle.addEventListener('click', () => {
40 const currentTheme = document.documentElement.classList.contains('dark')
41 ? 'dark'
42 : 'light';
43
44 const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
45 setTheme(newTheme);
46 });
47 }
48
49 // Listen for system theme changes
50 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
51 // Only auto-update if user hasn't set a preference
52 if (!localStorage.theme) {
53 applyTheme(e.matches ? 'dark' : 'light');
54 }
55 });
56 });
57})();3. Three-Way Toggle (Light / Dark / System):
1<select id="theme-select" class="bg-white dark:bg-gray-800">
2 <option value="light">Light</option>
3 <option value="dark">Dark</option>
4 <option value="system">System</option>
5</select>1const themeSelect = document.getElementById('theme-select');
2
3themeSelect.addEventListener('change', (e) => {
4 const theme = e.target.value;
5
6 if (theme === 'system') {
7 localStorage.removeItem('theme');
8 applyTheme(getSystemTheme());
9 } else {
10 setTheme(theme);
11 }
12});4. Smooth Transitions:
1/* Add to your CSS for smooth theme transitions */
2* {
3 transition-property: background-color, border-color, color, fill, stroke;
4 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
5 transition-duration: 150ms;
6}Guidelines for implementing accessible, performant dark mode.
1. Accessibility Requirements:
1<!-- Ensure sufficient contrast ratios -->
2<!-- WCAG AA: 4.5:1 for normal text, 3:1 for large text -->
3
4<!-- Good contrast in light mode -->
5<p class="text-gray-900 bg-white">
6 Contrast ratio: 20.6:1 ✓
7</p>
8
9<!-- Good contrast in dark mode -->
10<p class="dark:text-gray-100 dark:bg-gray-900">
11 Contrast ratio: 17.9:1 ✓
12</p>
13
14<!-- Bad - insufficient contrast -->
15<p class="text-gray-400 bg-white dark:text-gray-600 dark:bg-gray-900">
16 Low contrast ✗
17</p>2. Focus States in Dark Mode:
1<button class="
2 focus:outline-none
3 focus:ring-2
4 focus:ring-blue-500 dark:focus:ring-blue-400
5 focus:ring-offset-2
6 focus:ring-offset-white dark:focus:ring-offset-gray-900
7">
8 Accessible focus ring in both modes
9</button>3. Form Inputs:
1<input
2 type="text"
3 class="
4 w-full px-4 py-2
5 bg-white dark:bg-gray-800
6 border border-gray-300 dark:border-gray-600
7 text-gray-900 dark:text-white
8 placeholder-gray-400 dark:placeholder-gray-500
9 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400
10 focus:border-transparent
11 rounded-lg
12 "
13 placeholder="Enter text..."
14>4. Performance Optimization:
1// Prevent flash of unstyled content (FOUC)
2// Add to <head> before any content loads
3<script>
4 // Check theme immediately, before page renders
5 if (localStorage.theme === 'dark' ||
6 (!('theme' in localStorage) &&
7 window.matchMedia('(prefers-color-scheme: dark)').matches)) {
8 document.documentElement.classList.add('dark');
9 }
10</script>5. Testing Checklist:
1Dark Mode Testing:
2
3✓ Text has sufficient contrast (4.5:1 minimum)
4✓ Focus states are visible in both modes
5✓ Form inputs are styled for both modes
6✓ Images look good in both modes
7✓ Shadows are appropriate for both modes
8✓ Brand colors work in both modes
9✓ Links are distinguishable in both modes
10✓ Loading states work in both modes
11✓ Error states work in both modes
12✓ Success states work in both modes
13✓ No flash of unstyled content (FOUC)
14✓ Theme persists across page loads
15✓ Respects system preference initially
16✓ Accessible theme switcher
17✓ Works with all responsive breakpoints6. Common Pitfalls to Avoid:
1<!-- ❌ DON'T: Pure black backgrounds -->
2<div class="dark:bg-black">
3 Too harsh on eyes
4</div>
5
6<!-- ✅ DO: Dark gray backgrounds -->
7<div class="dark:bg-gray-900">
8 Easier on eyes
9</div>
10
11<!-- ❌ DON'T: Pure white text on dark -->
12<p class="dark:text-white dark:bg-gray-900">
13 Too much contrast
14</p>
15
16<!-- ✅ DO: Off-white text -->
17<p class="dark:text-gray-100 dark:bg-gray-900">
18 Comfortable reading
19</p>
20
21<!-- ❌ DON'T: Forget about shadows -->
22<div class="shadow-lg">
23 Invisible in dark mode!
24</div>
25
26<!-- ✅ DO: Adjust shadows for dark mode -->
27<div class="shadow-lg dark:shadow-2xl dark:shadow-black/50">
28 Visible in both modes
29</div>7. SEO and Meta Tags:
1<!-- Support for browser theme color -->
2<meta
3 name="theme-color"
4 content="#ffffff"
5 media="(prefers-color-scheme: light)"
6>
7<meta
8 name="theme-color"
9 content="#111827"
10 media="(prefers-color-scheme: dark)"
11>Build a fully functional dark mode application with theme switcher, proper color schemes, and best practices implementation.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Dark Mode Demo</title>
7 <script src="https://cdn.tailwindcss.com"></script>
8 <script>
9 tailwind.config = {
10 darkMode: 'class',
11 }
12 </script>
13
14 <!-- Prevent FOUC - Apply theme before render -->
15 <script>
16 if (localStorage.theme === 'dark' ||
17 (!('theme' in localStorage) &&
18 window.matchMedia('(prefers-color-scheme: dark)').matches)) {
19 document.documentElement.classList.add('dark');
20 }
21 </script>
22</head>
23<body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-200">
24 <!-- Header with Theme Switcher -->
25 <header class="bg-white dark:bg-gray-800 shadow-lg dark:shadow-2xl dark:shadow-black/20 sticky top-0 z-50 transition-colors">
26 <nav class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
27 <div class="flex justify-between items-center h-16">
28 <div class="flex items-center space-x-2">
29 <div class="h-8 w-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg"></div>
30 <span class="text-xl font-bold text-gray-900 dark:text-white">
31 DarkMode App
32 </span>
33 </div>
34
35 <!-- Theme Toggle Button -->
36 <button
37 id="theme-toggle"
38 class="
39 p-2 rounded-lg
40 bg-gray-200 dark:bg-gray-700
41 text-gray-800 dark:text-gray-200
42 hover:bg-gray-300 dark:hover:bg-gray-600
43 transition-colors
44 focus:outline-none
45 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400
46 focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-gray-800
47 "
48 aria-label="Toggle dark mode"
49 >
50 <!-- Sun Icon -->
51 <svg id="sun-icon" class="hidden dark:block w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
52 <path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" />
53 </svg>
54 <!-- Moon Icon -->
55 <svg id="moon-icon" class="block dark:hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
56 <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
57 </svg>
58 </button>
59 </div>
60 </nav>
61 </header>
62
63 <!-- Main Content -->
64 <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
65 <!-- Hero Section -->
66 <section class="text-center mb-16">
67 <h1 class="text-4xl sm:text-5xl lg:text-6xl font-extrabold text-gray-900 dark:text-white mb-6 transition-colors">
68 Dark Mode with Tailwind CSS
69 </h1>
70 <p class="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto mb-8 transition-colors">
71 A complete dark mode implementation with theme persistence, smooth transitions, and accessibility features.
72 </p>
73 <div class="flex flex-col sm:flex-row gap-4 justify-center">
74 <button class="
75 bg-blue-600 hover:bg-blue-700
76 dark:bg-blue-500 dark:hover:bg-blue-600
77 text-white px-8 py-3 rounded-lg font-semibold
78 transition-colors
79 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
80 dark:focus:ring-offset-gray-900
81 ">
82 Get Started
83 </button>
84 <button class="
85 bg-gray-200 hover:bg-gray-300
86 dark:bg-gray-700 dark:hover:bg-gray-600
87 text-gray-900 dark:text-white
88 px-8 py-3 rounded-lg font-semibold
89 transition-colors
90 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2
91 dark:focus:ring-offset-gray-900
92 ">
93 Learn More
94 </button>
95 </div>
96 </section>
97
98 <!-- Feature Cards -->
99 <section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
100 <!-- Card 1 -->
101 <div class="
102 bg-white dark:bg-gray-800
103 border border-gray-200 dark:border-gray-700
104 rounded-xl shadow-lg dark:shadow-2xl dark:shadow-black/20
105 p-6 transition-all
106 hover:shadow-xl dark:hover:shadow-black/30
107 ">
108 <div class="h-12 w-12 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center mb-4">
109 <svg class="w-6 h-6 text-blue-600 dark:text-blue-400" fill="currentColor" viewBox="0 0 20 20">
110 <path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
111 </svg>
112 </div>
113 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">
114 Auto Detection
115 </h3>
116 <p class="text-gray-600 dark:text-gray-300">
117 Automatically detects and respects your system's dark mode preference.
118 </p>
119 </div>
120
121 <!-- Card 2 -->
122 <div class="
123 bg-white dark:bg-gray-800
124 border border-gray-200 dark:border-gray-700
125 rounded-xl shadow-lg dark:shadow-2xl dark:shadow-black/20
126 p-6 transition-all
127 hover:shadow-xl dark:hover:shadow-black/30
128 ">
129 <div class="h-12 w-12 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center mb-4">
130 <svg class="w-6 h-6 text-purple-600 dark:text-purple-400" fill="currentColor" viewBox="0 0 20 20">
131 <path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/>
132 </svg>
133 </div>
134 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">
135 Manual Toggle
136 </h3>
137 <p class="text-gray-600 dark:text-gray-300">
138 Override system preference with manual theme switcher.
139 </p>
140 </div>
141
142 <!-- Card 3 -->
143 <div class="
144 bg-white dark:bg-gray-800
145 border border-gray-200 dark:border-gray-700
146 rounded-xl shadow-lg dark:shadow-2xl dark:shadow-black/20
147 p-6 transition-all
148 hover:shadow-xl dark:hover:shadow-black/30
149 ">
150 <div class="h-12 w-12 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center mb-4">
151 <svg class="w-6 h-6 text-green-600 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20">
152 <path d="M8 16.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM15 16.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z"/><path d="M3 4a1 1 0 00-1 1v10a1 1 0 001 1h1.05a2.5 2.5 0 014.9 0H10a1 1 0 001-1V5a1 1 0 00-1-1H3zM14 7a1 1 0 00-1 1v6.05A2.5 2.5 0 0115.95 16H17a1 1 0 001-1v-5a1 1 0 00-.293-.707l-2-2A1 1 0 0015 7h-1z"/>
153 </svg>
154 </div>
155 <h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">
156 Persisted
157 </h3>
158 <p class="text-gray-600 dark:text-gray-300">
159 Your theme preference is saved and persists across sessions.
160 </p>
161 </div>
162 </section>
163
164 <!-- Form Example -->
165 <section class="mb-16">
166 <div class="
167 bg-white dark:bg-gray-800
168 border border-gray-200 dark:border-gray-700
169 rounded-xl shadow-lg
170 p-8
171 max-w-2xl mx-auto
172 ">
173 <h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">
174 Newsletter Subscription
175 </h2>
176
177 <form class="space-y-4">
178 <div>
179 <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
180 Email Address
181 </label>
182 <input
183 type="email"
184 class="
185 w-full px-4 py-2
186 bg-white dark:bg-gray-700
187 border border-gray-300 dark:border-gray-600
188 text-gray-900 dark:text-white
189 placeholder-gray-400 dark:placeholder-gray-500
190 rounded-lg
191 focus:outline-none
192 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400
193 focus:border-transparent
194 "
195 placeholder="[email protected]"
196 />
197 </div>
198
199 <div>
200 <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
201 Interests
202 </label>
203 <select class="
204 w-full px-4 py-2
205 bg-white dark:bg-gray-700
206 border border-gray-300 dark:border-gray-600
207 text-gray-900 dark:text-white
208 rounded-lg
209 focus:outline-none
210 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400
211 focus:border-transparent
212 ">
213 <option>Web Development</option>
214 <option>Design</option>
215 <option>Marketing</option>
216 </select>
217 </div>
218
219 <div class="flex items-center">
220 <input
221 type="checkbox"
222 id="newsletter"
223 class="
224 h-4 w-4
225 text-blue-600 dark:text-blue-500
226 border-gray-300 dark:border-gray-600
227 rounded
228 focus:ring-blue-500 dark:focus:ring-blue-400
229 "
230 />
231 <label for="newsletter" class="ml-2 text-sm text-gray-700 dark:text-gray-300">
232 I agree to receive weekly newsletters
233 </label>
234 </div>
235
236 <button type="submit" class="
237 w-full
238 bg-blue-600 hover:bg-blue-700
239 dark:bg-blue-500 dark:hover:bg-blue-600
240 text-white px-6 py-3 rounded-lg font-semibold
241 transition-colors
242 focus:outline-none
243 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
244 dark:focus:ring-offset-gray-800
245 ">
246 Subscribe
247 </button>
248 </form>
249 </div>
250 </section>
251 </main>
252
253 <!-- Footer -->
254 <footer class="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 py-12 transition-colors">
255 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
256 <p class="text-gray-600 dark:text-gray-400">
257 © 2024 Dark Mode Demo. Built with Tailwind CSS.
258 </p>
259 </div>
260 </footer>
261
262 <!-- Theme Switcher Script -->
263 <script>
264 (function() {
265 const themeToggle = document.getElementById('theme-toggle');
266
267 const applyTheme = (theme) => {
268 if (theme === 'dark') {
269 document.documentElement.classList.add('dark');
270 } else {
271 document.documentElement.classList.remove('dark');
272 }
273 };
274
275 const setTheme = (theme) => {
276 localStorage.theme = theme;
277 applyTheme(theme);
278 };
279
280 themeToggle?.addEventListener('click', () => {
281 const isDark = document.documentElement.classList.contains('dark');
282 setTheme(isDark ? 'light' : 'dark');
283 });
284
285 // Listen for system theme changes
286 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
287 if (!localStorage.theme) {
288 applyTheme(e.matches ? 'dark' : 'light');
289 }
290 });
291 })();
292 </script>
293</body>
294</html>This complete dark mode application demonstrates:
Key Features:
Excellent! You now know how to implement production-ready dark mode with Tailwind CSS.
Next Lesson: Plugins and Extensions
You'll learn how to:
Practice Challenge:
Build a complete dashboard with dark mode:
Test thoroughly in both light and dark modes. Ensure all interactive elements have proper focus states and contrast!
Your progress is saved in this browser, on this device only.