Tailwind CSS Dark Mode with Example

by Sabbir Hossain

Dark mode has become an essential feature in modern web applications, providing users with a comfortable viewing experience in low-light environments while reducing eye strain. Tailwind CSS makes implementing dark mode surprisingly straightforward with its built-in dark mode utilities. In this comprehensive guide, we'll explore how to create a seamless light and dark theme toggle using Tailwind CSS.

Understanding Tailwind's Dark Mode Strategy

Tailwind CSS uses a class-based approach for dark mode implementation. By default, it applies the dark: variant when a parent element (typically html or body) has the dark class. This approach gives developers complete control over when and how dark mode is activated.

The Dark Mode Workflow

  1. Detection: Check if dark mode is currently active
  2. Toggle: Switch between light and dark themes
  3. Persistence: Store the user's preference in localStorage
  4. Application: Apply the appropriate styles using Tailwind's dark variants

Breaking Down the Implementation

Let's examine a practical implementation that demonstrates these concepts:

const DarkMode = () => {
  const handleTheme = () => {
    const currentTheme = document.documentElement.classList.contains("dark")
      ? "light"
      : "dark";
    document.documentElement.classList.toggle("dark");
    localStorage.setItem("theme", currentTheme);
  };

  return (
    <div className="flex min-h-screen items-center justify-center bg-neutral-200 bg-[image:repeating-linear-gradient(315deg,_var(--pattern-fg)_0,_var(--pattern-fg)_1px,_transparent_0,_transparent_50%)] bg-[size:10px_10px] bg-fixed text-white transition duration-200 ease-in-out [--pattern-fg:rgb(0,0,0,0.05)] dark:bg-neutral-950 dark:text-black dark:[--pattern-fg:rgb(255,255,255,0.1)]">
      <div className="relative h-80 w-64 overflow-hidden rounded-lg bg-neutral-300 shadow-black dark:shadow-neutral-600 shadow-sm dark:bg-neutral-900">
        <img
          src="https://images.unsplash.com/photo-1628260412297-a3377e45006f?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
          alt="cartoon image"
          className="mask-b-from-80%"
        />
        <div className="p-4">
          <h4 className="text-lg font-bold text-neutral-900 dark:text-neutral-100">
            Cartoon Image
          </h4>
          <p className="text-xs text-neutral-700 dark:text-neutral-300">
            A fun cartoon image of a landscape.
          </p>
        </div>
        <button
          onClick={handleTheme}
          className="ml-4 cursor-pointer rounded bg-neutral-800 px-3 py-1 font-medium text-white hover:bg-neutral-700 dark:bg-neutral-200 dark:text-neutral-900 dark:hover:bg-neutral-300"
        >
          Click Me
        </button>
      </div>
    </div>
  );
};

Key Components Explained

1. Theme Toggle Function

The handleTheme function is the heart of our dark mode implementation:

const handleTheme = () => {
  const currentTheme = document.documentElement.classList.contains("dark")
    ? "light"
    : "dark";
  document.documentElement.classList.toggle("dark");
  localStorage.setItem("theme", currentTheme);
};

This function performs three crucial tasks:

  • Detects the current theme by checking for the dark class
  • Toggles the theme by adding/removing the dark class from the document element
  • Persists the user's preference in localStorage for future visits

2. Advanced Background Patterns

The component showcases sophisticated background styling using CSS custom properties:

className =
  "bg-[image:repeating-linear-gradient(315deg,_var(--pattern-fg)_0,_var(--pattern-fg)_1px,_transparent_0,_transparent_50%)] bg-[size:10px_10px] bg-fixed [--pattern-fg:rgb(0,0,0,0.05)] dark:[--pattern-fg:rgb(255,255,255,0.1)]";

This creates a subtle diagonal pattern that automatically adjusts its opacity based on the current theme:

  • Light mode: Dark pattern with 5% opacity
  • Dark mode: Light pattern with 10% opacity

3. Comprehensive Dark Mode Variants

Every visual element includes both light and dark mode styles:

  • Backgrounds: bg-neutral-200 dark:bg-neutral-950
  • Text colors: text-white dark:text-black
  • Shadows: shadow-black dark:shadow-neutral-600
  • Interactive states: hover:bg-neutral-700 dark:hover:bg-neutral-300

Setting Up Tailwind Dark Mode

To use dark mode in your Tailwind project, ensure your tailwind.config.js includes:

module.exports = {
  darkMode: "class", // Enable class-based dark mode
  // ... other config
};

Best Practices for Dark Mode Implementation

1. Consistent Color Schemes

Use Tailwind's neutral color palette for consistent theming:

  • Light mode: neutral-50 to neutral-300 for backgrounds
  • Dark mode: neutral-700 to neutral-950 for backgrounds
  • Ensure sufficient contrast ratios for accessibility

2. Smooth Transitions

Add transitions to create polished theme switching:

className = "transition duration-200 ease-in-out";

3. System Preference Detection

For enhanced user experience, detect and respect system preferences:

const getInitialTheme = () => {
  if (typeof window !== "undefined") {
    const savedTheme = localStorage.getItem("theme");
    if (savedTheme) return savedTheme;

    return window.matchMedia("(prefers-color-scheme: dark)").matches
      ? "dark"
      : "light";
  }
  return "light";
};

4. Image and Media Handling

Consider how images appear in different themes. You might need:

  • Different images for different themes
  • CSS filters to adjust brightness/contrast
  • Overlay adjustments for better integration

Advanced Techniques

CSS Custom Properties for Dynamic Theming

The example demonstrates using CSS custom properties with Tailwind's arbitrary value syntax:

[--pattern-fg:rgb(0,0,0,0.05)] dark:[--pattern-fg:rgb(255,255,255,0.1)]

This technique allows for dynamic color adjustments that go beyond Tailwind's predefined colors.

Responsive Dark Mode

Combine dark mode with responsive design:

className = "bg-white md:bg-gray-100 dark:bg-gray-900 dark:md:bg-gray-800";

Accessibility Considerations

When implementing dark mode, remember:

  • Maintain sufficient color contrast (WCAG guidelines)
  • Test with screen readers in both modes
  • Provide clear visual feedback for the theme toggle
  • Consider users with photosensitivity

Conclusion

Tailwind CSS makes dark mode implementation both powerful and straightforward. By combining class-based theme switching with localStorage persistence and thoughtful color choices, you can create a seamless user experience that respects user preferences and system settings.

The key to successful dark mode implementation lies in:

  • Systematic application of dark variants across all components
  • Smooth transitions between themes
  • Persistent user preferences
  • Accessibility-conscious design choices

Start with the basic toggle functionality and gradually enhance your implementation with advanced features like system preference detection and custom CSS properties for truly dynamic theming.


Ready to implement dark mode in your next project? The combination of Tailwind's utility classes and JavaScript theme management provides everything you need for a professional, user-friendly dark mode experience.