Tailwind CSS Cheatsheet

Dark Mode

Use this Tailwind CSS reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

How Dark Mode Works

Tailwind's dark: variant activates dark-mode styles. The trigger strategy determines when it activates.

Strategy: Media Query (default)

Activates when the OS/browser is set to dark mode. No configuration needed in v4; in v3 this is the default.

<div class="bg-white text-slate-900 dark:bg-slate-900 dark:text-white">
  Adapts to OS preference
</div>

Strategy: Class Toggle (v3)

Activate dark mode by adding a class to <html> — gives you manual control.

// tailwind.config.js (v3)
module.exports = {
  darkMode: 'class',
  // ...
}
<!-- Add 'dark' class to html element to activate -->
<html class="dark">
  <body>
    <div class="dark:bg-slate-900">Dark background</div>
  </body>
</html>

Toggle via JavaScript:

<script>
  // Toggle dark mode
  document.documentElement.classList.toggle('dark')

  // Set from localStorage
  if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
    document.documentElement.classList.add('dark')
  } else {
    document.documentElement.classList.remove('dark')
  }
</script>

Strategy: Data Attribute (v3.4+ / v4)

Use a data-* attribute on the root element instead of a class — avoids class conflicts.

// tailwind.config.js (v3.4+)
module.exports = {
  darkMode: ['selector', '[data-theme="dark"]'],
}
/* v4 — in your CSS */
@import "tailwindcss";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
<html data-theme="dark">
  <div class="dark:bg-slate-900">Activated by data attribute</div>
</html>

Strategy: Class Toggle (v4)

Redefine the dark variant to key off a class instead of the media query:

@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
<div class="dark">
  <p class="text-slate-900 dark:text-white">Only dark inside .dark container</p>
</div>

Using the dark: Variant

Any utility can be prefixed with dark::

<!-- Colors -->
<body class="bg-white text-slate-900 dark:bg-slate-950 dark:text-slate-50">

<!-- Borders -->
<div class="border border-slate-200 dark:border-slate-700">

<!-- Shadows -->
<div class="shadow-md dark:shadow-slate-900/50">

<!-- Typography -->
<p class="text-slate-600 dark:text-slate-400">Muted text</p>
<h2 class="text-slate-900 dark:text-white">Heading</h2>

<!-- Backgrounds with opacity -->
<div class="bg-slate-100 dark:bg-slate-800">Surface</div>
<div class="bg-white/80 dark:bg-slate-900/80">Translucent</div>

<!-- Interactive states in dark mode -->
<button class="bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400 text-white">
  Button
</button>

Common Dark Mode Patterns

Page background and text

<html class="bg-white text-slate-900 dark:bg-slate-950 dark:text-slate-50">

Card component

<div class="bg-white border border-slate-200 rounded-xl shadow-sm p-6
            dark:bg-slate-800 dark:border-slate-700 dark:shadow-slate-900/50">
  <h3 class="font-semibold text-slate-900 dark:text-white">Card Title</h3>
  <p class="text-slate-600 dark:text-slate-400 mt-1">Card description</p>
</div>

Input field

<input
  class="w-full border border-slate-300 rounded-lg px-3 py-2
         bg-white text-slate-900 placeholder:text-slate-400
         focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
         dark:bg-slate-800 dark:border-slate-600 dark:text-white dark:placeholder:text-slate-500"
  type="text"
  placeholder="Search..."
>

Navigation

<nav class="bg-white border-b border-slate-200 dark:bg-slate-900 dark:border-slate-700">
  <a href="/" class="text-slate-700 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white">
    Home
  </a>
</nav>

Code block

<pre class="bg-slate-100 dark:bg-slate-900 rounded-lg p-4 text-sm overflow-x-auto">
  <code class="text-slate-800 dark:text-slate-200">code here</code>
</pre>

Badge / pill

<span class="bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400
             text-xs font-medium px-2.5 py-0.5 rounded-full">
  Badge
</span>

Frosted glass in dark mode

<div class="bg-white/80 backdrop-blur-md border border-slate-200/60 rounded-2xl
            dark:bg-slate-900/80 dark:border-slate-700/60">
  Frosted glass
</div>

Prevent Flash of Wrong Theme

Add an inline script before any CSS loads to read the stored preference:

<head>
  <script>
    // Prevent FOUC
    const theme = localStorage.getItem('theme')
    if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
      document.documentElement.classList.add('dark')
    }
  </script>
  <!-- CSS loads after — no flash -->
</head>

Combining dark: with Other Variants

Dark mode stacks with any other variant:

<!-- dark + hover -->
<button class="dark:hover:bg-slate-700">...</button>

<!-- dark + responsive -->
<div class="dark:md:text-lg">...</div>

<!-- dark + focus -->
<input class="dark:focus:ring-blue-400">

<!-- dark + group-hover -->
<div class="group">
  <p class="group-hover:text-blue-600 dark:group-hover:text-blue-400">...</p>
</div>

<!-- dark + disabled -->
<button class="dark:disabled:opacity-40" disabled>...</button>

<!-- dark + first-child -->
<li class="dark:first:border-t-0">...</li>

System-Level vs. Manual Toggle

Using both (respect system unless user has picked)

<script>
  // On page load
  function applyTheme(theme) {
    if (theme === 'dark') {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
  }

  const stored = localStorage.getItem('theme')
  if (stored) {
    applyTheme(stored)
  } else {
    // System preference
    applyTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
  }
</script>

Theme toggle button

<button
  onclick="
    const isDark = document.documentElement.classList.toggle('dark');
    localStorage.setItem('theme', isDark ? 'dark' : 'light');
  "
  class="p-2 rounded-lg text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white transition-colors"
  aria-label="Toggle dark mode"
>
  <!-- Sun icon (shown in dark mode) -->
  <svg class="hidden dark:block size-5" ...></svg>
  <!-- Moon icon (shown in light mode) -->
  <svg class="block dark:hidden size-5" ...></svg>
</button>

Swapping Images for Dark Mode

<!-- CSS display swap approach -->
<img class="block dark:hidden" src="logo-light.png" alt="Logo">
<img class="hidden dark:block" src="logo-dark.png" alt="Logo">

<!-- CSS filter approach (quick invert) -->
<img class="dark:invert" src="logo-black.svg" alt="Logo">

<!-- picture element (no JS, respects media query) -->
<picture>
  <source srcset="logo-dark.png" media="(prefers-color-scheme: dark)">
  <img src="logo-light.png" alt="Logo">
</picture>

Dark Mode Color Palette Tips

LightDark equivalent
bg-whitedark:bg-slate-950
bg-slate-50 (surface)dark:bg-slate-900
bg-slate-100 (raised)dark:bg-slate-800
text-slate-900 (primary)dark:text-white
text-slate-700 (body)dark:text-slate-300
text-slate-500 (muted)dark:text-slate-400
border-slate-200dark:border-slate-700
border-slate-100dark:border-slate-800

Using CSS variables for dark mode tokens (v4 recommended approach) — register the tokens once in @theme, then override the plain CSS variables under your dark selector:

@theme {
  --color-surface: #ffffff;
  --color-on-surface: #0f172a;
}

@layer base {
  .dark {
    --color-surface: #0f172a;
    --color-on-surface: #f8fafc;
  }
}
<div class="bg-surface text-on-surface">Themed automatically</div>