Skip to main content
TailwindCSS Basics
CHAPTER 16 Beginner

Tailwind CSS Dark Mode

Updated: May 12, 2026
20 min read

# 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 media or class based 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 your tailwind.config.js, you must choose how dark mode triggers:
  1. 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.
  1. 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):
javascript
12345678910111213141516171819202122
  module.exports = {
    darkMode: &#039;class&#039;, // or 'media'
    // ...
  }
  ```
- Usage: `dark:{utility}` (e.g., `dark:bg-gray-800`, `dark:text-white`, `dark:border-gray-700`).

## 5. Real-World Examples

A standard card component in a SaaS app:
`<div class="bg-white border-gray-200 text-gray-900 dark:bg-slate-800 dark:border-slate-700 dark:text-white">`
- Light mode: White background, light gray border, dark text.
- Dark mode: Dark slate background, dark gray border, white text.

## 6. Multiple Code Examples

Let&#039;s see how elements transform.

*(Note: To test these in a Tailwind Play environment or local setup using &#039;class&#039; strategy, add `class="dark"` to a parent wrapping `<div>` or your `<html>` tag).*

### Example 1: Basic Text and Background Inversion

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>

1234
### Example 2: Handling Borders and Dividers

In dark mode, borders should be very subtle to separate dark elements.

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>

1234
### Example 3: Buttons in Dark Mode

Primary buttons (like a brand blue) often look fine in both modes, but secondary buttons (white) need inversion.

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>

1234
### Example 4: Input Fields in Dark Mode

Inputs need careful attention to backgrounds, borders, text, and placeholders.

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>

1234
### Example 5: Hover States with Dark Mode

You can stack modifiers! `dark:hover:bg-gray-700`.

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>

1234
### Example 6: Dark Mode Elevations (Shadows vs Backgrounds)

In light mode, we use shadows (`shadow-md`) to show depth. In dark mode, shadows are hard to see against a dark background. Instead, we use *lighter shades of gray* to show elevation.

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>

12345678910111213141516171819202122232425
## 7. Output Explanations

In **Example 6 (Elevations)**, notice a critical design principle: `shadow-lg dark:shadow-none`. 
In light mode, the card uses a shadow to pop off the page. In dark mode, it removes the shadow completely! Why? Because a black shadow on a dark gray background looks muddy and unprofessional. To show elevation in dark mode, you make the card's background slightly *lighter* than the main background (e.g., body is `gray-900`, card is `gray-800`), and perhaps add a very subtle border (`dark:border-gray-700`).

## 8. Common Mistakes

1. **Forgetting to configure `tailwind.config.js`:** If you add the `<html class="dark">` class but nothing happens, it's because Tailwind defaults to OS-level `media` query dark mode. You must set `darkMode: 'class'` in your config.
2. **Pure Black/White:** Using `dark:bg-black dark:text-white`. This causes severe eye strain. Use `dark:bg-slate-900 dark:text-slate-200`.
3. **Inverting brand colors:** If your primary button is `bg-blue-600`, you usually don't need to change it in dark mode. Only backgrounds, texts, and borders usually need inversion.

## 9. Best Practices

- **Use the Slate or Gray palette:** Tailwind's `slate`, `gray`, `zinc`, `neutral`, and `stone` palettes are expertly calibrated for dark modes. Choose one and stick to it.
- **Text contrast:** Main headings can be `dark:text-white`. Paragraph text should be softer, like `dark:text-gray-300` or `dark:text-slate-400`.

## 10. Exercises

1. Create a simple "Light/Dark" toggle button. In light mode it should be a yellow background with black text (Sun). In dark mode, it should be a dark purple background with yellow text (Moon).
2. Create a pricing card. Ensure the border color, background color, text colors, and the CTA button all look perfect in both modes.

## 11. Mini Project: Dark Mode Dashboard

Let's build a mini-dashboard card that perfectly supports both modes.

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

Question 1

If your tailwind.config.js has darkMode: 'class', how do you trigger dark mode?

Question 2

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.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·