Tailwind CSS Dark Mode
# Chapter 16: Tailwind CSS Dark Mode
Dark mode is no longer an optional feature; for many users, it is a strict requirement. Staring at bright white screens all day causes eye strain, and a beautifully implemented dark mode provides a premium, comfortable experience.
In traditional CSS, implementing dark mode involves writing complex CSS variables or entirely separate stylesheets. Tailwind CSS simplifies this massively with the dark: variant.
---
1. Introduction
The dark: variant in Tailwind works exactly like the hover: or md: variants. You prefix a utility class with dark:, and that style will only apply when dark mode is active.
Example: bg-white dark:bg-slate-900.
This tells the browser: "The background is white. But if dark mode is active, the background is dark slate."
2. Learning Objectives
By the end of this chapter, you will be able to:
-
Configure Tailwind to use
mediaorclassbased dark mode.
-
Use the
dark:variant to invert colors on backgrounds, text, and borders.
- Understand the design philosophy of dark mode (it's not just "make everything black").
- Build a toggleable dark mode dashboard.
3. Beginner-Friendly Explanations
Media vs. Class Strategy
In yourtailwind.config.js, you must choose how dark mode triggers:
-
1.
media(Default): Tailwind listens to the user's operating system settings (Windows/macOS dark mode). If their OS is dark, your website is dark.
-
2.
class(Recommended): Tailwind only turns on dark mode if a specific class (class="dark") is present on the<html>tag of your website. This allows you to build a "Light / Dark" toggle button in your app.
Dark Mode is NOT Pure Black
Good dark mode design rarely uses#000000 (pure black). Pure black with pure white text creates extreme contrast that causes astigmatism-like "halos" for readers. Instead, use dark grays like bg-slate-900 or bg-gray-900.
4. Syntax Explanation
-
Configuration (in
tailwind.config.js):
html <!-- TailwindCSS Example --> <!-- Add 'dark' class to the parent to see the effect --> <div class="dark p-8"> <div class="bg-white dark:bg-slate-900 p-6 rounded-lg border border-gray-200 dark:border-slate-700"> <h2 class="text-gray-900 dark:text-white text-xl font-bold">Hello World</h2> <p class="text-gray-600 dark:text-gray-400 mt-2">This text adapts to the current theme.</p> </div> </div>
html <!-- TailwindCSS Example --> <div class="dark p-8"> <div class="bg-white dark:bg-gray-800 rounded shadow p-4"> <div class="pb-4 border-b border-gray-200 dark:border-gray-700"> <h3 class="text-black dark:text-white font-bold">Settings</h3> </div> <div class="pt-4"> <p class="text-gray-500 dark:text-gray-400">Account details go here.</p> </div> </div> </div>
html <!-- TailwindCSS Example --> <div class="dark p-8 flex gap-4 bg-gray-50 dark:bg-slate-900"> <!-- Primary (Usually unchanged) --> <button class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-2 px-4 rounded"> Save </button> <!-- Secondary (Needs inversion) --> <button class="bg-white dark:bg-slate-800 text-gray-700 dark:text-gray-200 border border-gray-300 dark:border-slate-600 hover:bg-gray-50 dark:hover:bg-slate-700 font-medium py-2 px-4 rounded"> Cancel </button> </div>
html <!-- TailwindCSS Example --> <div class="dark p-8 bg-white dark:bg-gray-900"> <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label> <input type="email" placeholder="you@example.com" class="w-full px-4 py-2 rounded border outline-none transition-colors bg-white border-gray-300 text-gray-900 placeholder-gray-400 focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:placeholder-gray-500 dark:focus:ring-blue-400"> </div>
html <!-- TailwindCSS Example --> <div class="dark p-8"> <a href="#" class="block p-4 rounded-lg bg-gray-100 hover:bg-gray-200 dark:bg-slate-800 dark:hover:bg-slate-700 transition"> <h4 class="text-black dark:text-white font-bold">Interactive Link</h4> </a> </div>
html <!-- TailwindCSS Example --> <div class="dark p-8 bg-gray-100 dark:bg-gray-900"> <!-- Base background is gray-900. Elevated card is gray-800. --> <div class="bg-white dark:bg-gray-800 shadow-lg dark:shadow-none p-6 rounded-xl border border-transparent dark:border-gray-700"> <p class="text-black dark:text-white">Elevated Card</p> </div> </div>
html <!-- TailwindCSS Example: Dark Mode Dashboard Widget --> <!-- Wrap in a div with 'dark' class to preview dark mode --> <div class="dark"> <!-- Main Page Background --> <div class="min-h-screen bg-slate-50 dark:bg-slate-900 p-8 font-sans transition-colors duration-300"> <!-- Dashboard Widget --> <div class="max-w-md mx-auto bg-white dark:bg-slate-800 rounded-2xl shadow-xl dark:shadow-none border border-slate-200 dark:border-slate-700 overflow-hidden transition-colors duration-300"> <!-- Header --> <div class="px-6 py-4 border-b border-slate-100 dark:border-slate-700 flex justify-between items-center"> <h2 class="font-bold text-slate-800 dark:text-white">Server Analytics</h2> <span class="bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 text-xs font-bold px-2.5 py-1 rounded-full border border-transparent dark:border-emerald-800/50"> Online </span> </div>
<!-- Content --> <div class="p-6"> <div class="flex items-end gap-2 mb-6"> <span class="text-4xl font-extrabold text-slate-900 dark:text-white">99.9%</span> <span class="text-sm font-medium text-slate-500 dark:text-slate-400 mb-1">Uptime this month</span> </div>
<!-- Progress Bar --> <div class="w-full bg-slate-100 dark:bg-slate-700 rounded-full h-2.5 mb-2"> <div class="bg-indigo-600 dark:bg-indigo-500 h-2.5 rounded-full" style="width: 85%"></div> </div> <p class="text-xs text-slate-500 dark:text-slate-400 text-right">85% Storage Capacity</p> </div>
<!-- Footer Action --> <div class="px-6 py-4 bg-slate-50 dark:bg-slate-800/50 border-t border-slate-100 dark:border-slate-700"> <button class="w-full bg-white dark:bg-slate-700 border border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-200 font-semibold py-2 rounded-lg shadow-sm hover:bg-slate-50 dark:hover:bg-slate-600 transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400"> View Detailed Logs </button> </div>
</div>
</div>
</div>
``
Output Explanation:
Look closely at the "Online" badge: bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400.
In light mode, it uses a solid light green background. In dark mode, a light green background looks terribly bright and distracting. So we use emerald-900/30 (a very dark green with 30% opacity) and lighten the text (emerald-400). This is exactly how Vercel and GitHub design their dark mode badges.
12. Coding Challenges
Challenge: Create a navigation sidebar that has a light gray background in light mode, but becomes pure black (dark:bg-black) in dark mode, with a 1px right border (dark:border-slate-800) separating it from the main content.
13. MCQs with Answers
If your tailwind.config.js has darkMode: 'class', how do you trigger dark mode?
What is the recommended way to show "elevation" (like a floating card) in dark mode?
14. Interview Questions
Q: Explain how you would implement a "Light / Dark / System Default" theme toggle in a React/Tailwind application.
*Answer:* I would set darkMode: 'class' in Tailwind config. Then, I'd use Javascript to manage the state. If the user selects "Dark", I add the dark class to the <html> tag and save their preference in localStorage. If they select "System Default", I remove the class, check window.matchMedia('(prefers-color-scheme: dark)').matches, and add the dark class based on the OS result.
15. FAQs
Q: Do I have to write dark: for every single color?
A: Yes. Tailwind does not automatically invert colors for you. This is a good thing, as automatic inversion usually results in ugly, unpredictable interfaces. Explicitly declaring your dark mode colors gives you pixel-perfect control.
16. Summary
The dark:` variant is one of Tailwind's most powerful features. By appending it to background, text, and border utilities, you can build two completely separate color themes simultaneously within your HTML markup. Remember to use softer grays instead of pure black, and manage elevation with lighter backgrounds rather than heavy shadows.
17. Next Chapter Recommendation
Static pages are functional, but animations make them feel alive and premium. In Chapter 17: Tailwind CSS Animations and Transitions, we will learn how to add smooth hover transitions, loading spinners, and custom keyframe animations.