Tailwind CSS Dynamic Theme Switching Guide

published on 09 October 2024

Want to let users change your website's look instantly? Here's how to do it with Tailwind CSS:

  1. Set up Tailwind CSS for themes
  2. Create a theme context
  3. Build a theme toggle button
  4. Use CSS variables for flexible themes
  5. Implement user preference detection
  6. Save and load theme choices

Key benefits:

  • Improves accessibility
  • Enhances user experience
  • Maintains brand consistency

Quick setup:

npx create-react-app my-theme-switcher --template typescript
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Configure tailwind.config.js:

module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      colors: {
        primary: 'var(--color-primary)',
        secondary: 'var(--color-secondary)',
        background: 'var(--color-background)',
        text: 'var(--color-text)',
      },
    },
  },
  plugins: [],
}

This guide will show you how to create a smooth, accessible theme switcher using Tailwind CSS and React.

What you need to know first

Before we jump into dynamic theme switching with Tailwind CSS, let's cover the basics:

Tailwind CSS essentials

Tailwind CSS

You should know:

  • Utility classes
  • Responsive design
  • Customization options

Adam Wathan, Tailwind's creator, says: "Tailwind's utility-first approach makes it incredibly easy to build flexible, maintainable user interfaces."

JavaScript and React fundamentals

React

You'll need:

  • JavaScript basics
  • React components and hooks
  • State management

Setting up your environment

Here's a quick setup:

1. Install Node.js and npm

2. Create a new React project:

npx create-react-app my-theme-switcher --template typescript

3. Install Tailwind CSS:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

4. Configure Tailwind CSS:

Add to tailwind.config.js:

module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

5. Add Tailwind directives to src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

6. Start your server:

npm start

Now you're ready to build your dynamic theme switcher!

Setting up Tailwind CSS for dynamic themes

Here's how to set up Tailwind CSS for dynamic themes:

Configure Tailwind CSS

  1. Install Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
  1. Update tailwind.config.js:
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      colors: {
        primary: 'var(--color-primary)',
        secondary: 'var(--color-secondary)',
        background: 'var(--color-background)',
        text: 'var(--color-text)',
      },
    },
  },
  plugins: [],
}
  1. Add Tailwind directives to your main CSS file:
@tailwind base;
@tailwind components;
@tailwind utilities;

Create custom color schemes

  1. Define base colors in your main CSS file:
:root {
  --color-primary: 66, 133, 244;
  --color-secondary: 52, 168, 83;
  --color-background: 255, 255, 255;
  --color-text: 0, 0, 0;
}
  1. Create theme files (e.g., dark-theme.css):
[data-theme="dark"] {
  --color-primary: 138, 180, 248;
  --color-secondary: 129, 201, 149;
  --color-background: 32, 33, 36;
  --color-text: 255, 255, 255;
}
  1. Import theme files in your main CSS:
@import "themes/dark-theme.css";
@import "themes/light-theme.css";

To switch themes, use JavaScript:

document.documentElement.setAttribute('data-theme', 'dark');

This setup allows for easy theme switching and smooth transitions.

Building theme switching functionality

Let's create a theme switcher for your Tailwind CSS project. Here's how:

Make a theme context

First, set up a React context:

import { createContext, useContext, useState, useEffect } from "react";

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    document.documentElement.setAttribute(
      "data-theme",
      darkMode ? "dark" : "light"
    );
  }, [darkMode]);

  const toggleTheme = () => {
    setDarkMode((prevMode) => !prevMode);
  };

  return (
    <ThemeContext.Provider value={{ darkMode, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);

This context manages the theme state across your app.

Create a theme toggle button

Now, build a component for switching themes:

import { useTheme } from "./ThemeContext";

const ThemeToggle = () => {
  const { darkMode, toggleTheme } = useTheme();

  return (
    <button
      onClick={toggleTheme}
      className="px-4 py-2 rounded bg-gray-200 dark:bg-gray-700"
    >
      {darkMode ? "Light Mode" : "Dark Mode"}
    </button>
  );
};

Change themes on user action

Update your CSS:

:root {
  --bg-color: white;
  --text-color: black;
}

[data-theme="dark"] {
  --bg-color: black;
  --text-color: white;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
}

Wrap your app with the ThemeProvider:

import { ThemeProvider } from "./ThemeContext";

function App() {
  return (
    <ThemeProvider>
      <div className="App">
        <ThemeToggle />
        {/* Your app content */}
      </div>
    </ThemeProvider>
  );
}

Now users can switch themes with a click, instantly updating the UI.

sbb-itb-55aadfa

Using CSS Variables for Flexible Themes

CSS variables make theme switching in Tailwind CSS a breeze. Here's how to set them up:

Setting Theme Colors

In your global CSS file, define variables in the :root selector:

:root {
  --color-bgBase: 39deg 85% 95%;
  --color-content: 0deg 0% 20%;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bgBase: 240deg 27% 18%;
    --color-content: 210deg 14% 53%;
  }
}

Combining with Tailwind

In tailwind.config.js, add to the theme section:

module.exports = {
  theme: {
    colors: {
      bgBase: 'hsl(var(--color-bgBase) / <alpha-value>)',
      content: 'hsl(var(--color-content) / <alpha-value>)',
    }
  }
}

Now use these colors in components:

function SignIn() {
  return (
    <button className="py-1 px-2 bg-bgBase text-content rounded">
      Sign In
    </button>
  );
}

This approach eliminates the need for dark: classes on every component.

For more complex themes, define additional variables:

:root {
  --primary: #742a2a;
  --secondary: #e53e3e;
}

.th-blue {
  --primary: #2a4365;
  --secondary: #3182ce;
}

Use them in your Tailwind config:

module.exports = {
  theme: {
    colors: {
      'th-primary': 'var(--primary)',
      'th-secondary': 'var(--secondary)'
    }
  }
}

Apply themes easily in HTML:

<div class="bg-th-primary">
  <h3 class="bg-th-secondary text-th-primary">Join our mailing list</h3>
  <button class="bg-th-secondary" type="button">Sign up</button>
</div>

This method makes theme switching smooth and manageable.

Tips for good theme switching

Keep performance in mind

When adding theme switching, you need to make it fast. Here's how:

1. Use CSS variables for colors

This cuts down on the changes needed when switching themes.

2. Load only what you need

Only grab the CSS for the current theme. Load other stuff as needed.

3. Stop the flicker

Add this to your HTML's <head> to avoid that ugly flash of unstyled content:

const theme = localStorage.getItem('theme') || 'light';
document.documentElement.classList.add(theme);

4. Pick efficient selectors

Tailwind CSS is great for this. It makes tiny, fast CSS files.

Make themes accessible

Your themes should work for everyone. Here's how:

1. Check color contrast

Make sure text is easy to read on your background. Use tools like WebAIM's contrast checker.

2. Make it keyboard-friendly

Your theme toggle should work with a keyboard. Here's a good example:

<button type="button" id="theme-toggle" role="switch" aria-checked="false" class="flex w-14 h-8 p-1 rounded-full bg-slate-400 transition-colors duration-200 ease-in-out overflow-hidden outline-none focus-visible:ring focus-visible:ring-purple-400 aria-checked:bg-blue-400">
    <span class="w-6 h-6 rounded-full bg-white"></span>
</button>
<label for="theme-toggle">Toggle theme</label>

3. Help screen readers

Use ARIA attributes to tell screen readers about theme changes.

4. Follow user preferences

Use prefers-color-scheme to match the user's system color scheme:

@media (prefers-color-scheme: dark) {
  :root {
    --color-bgBase: 240deg 27% 18%;
    --color-content: 210deg 14% 53%;
  }
}

5. Keep it consistent

Don't change your layout between themes. It'll confuse people.

Working with user preferences

Let's dive into building a theme-switching feature with Tailwind CSS that respects user choices and system settings.

Checking system color scheme

Want to know if a user prefers dark mode? Use this:

const prefersDarkMode = () => window.matchMedia('(prefers-color-scheme: dark)').matches;

For light mode:

const prefersLightMode = () => window.matchMedia('(prefers-color-scheme: light)').matches;

These functions tap into the user's system settings. Simple, right?

Saving and loading user theme choices

To keep themes consistent, we'll use localStorage. Here's how:

// On page load
const savedTheme = localStorage.getItem('theme');
const systemPreference = prefersDarkMode() ? 'dark' : 'light';
const initialTheme = savedTheme || systemPreference;

document.documentElement.classList.add(initialTheme);

// When user changes theme
function toggleTheme() {
  const newTheme = document.documentElement.classList.contains('dark') ? 'light' : 'dark';
  document.documentElement.classList.remove('light', 'dark');
  document.documentElement.classList.add(newTheme);
  localStorage.setItem('theme', newTheme);
}

This code checks for a saved theme first, then falls back to the system preference.

Using React? Here's a hook-based approach:

function App() {
  const [theme, setTheme] = useState(() => {
    return localStorage.getItem('theme') || 'light';
  });

  useEffect(() => {
    localStorage.setItem('theme', theme);
    document.documentElement.classList.toggle('dark', theme === 'dark');
  }, [theme]);

  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Toggle Theme
    </button>
  );
}

This keeps the theme consistent across page reloads and respects user choices. Easy peasy!

Conclusion

Tailwind CSS dynamic theme switching is a powerful tool for web developers. It allows you to create designs that adapt to user preferences.

Here's what we've covered:

  • Tailwind CSS simplifies theme switching with its dark mode utility
  • CSS variables are crucial for flexible themes
  • Considering user preferences enhances the overall experience

Good theme switching goes beyond aesthetics:

  • It improves accessibility for users with different visual needs
  • It boosts user experience by offering choice
  • It maintains brand consistency across devices

Want to get started? Here's a simple approach:

1. Set up a basic light/dark toggle

2. Get user feedback

3. Expand your themes based on what you learn

Tailwind CSS provides the foundation. Now it's your turn to create themes that impress your users and make your site unique.

FAQs

How to toggle Tailwind dark mode?

Here's how to set up Tailwind dark mode:

1. Enable dark mode

Add this to your tailwind.config.js:

module.exports = {
  darkMode: 'class',
  // ...
}

2. Create a toggle

<input type="checkbox" id="dark-mode-toggle" class="light-switch">

3. Add JavaScript

const lightSwitches = document.querySelectorAll('.light-switch');

lightSwitches.forEach((lightSwitch, i) => {
  if (localStorage.getItem('dark-mode') === 'true') {
    lightSwitch.checked = true;
  }

  lightSwitch.addEventListener('change', () => {
    const { checked } = lightSwitch;

    lightSwitches.forEach((el, n) => {
      if (n !== i) el.checked = checked;
    });

    if (checked) {
      document.documentElement.classList.add('dark');
      localStorage.setItem('dark-mode', true);
    } else {
      document.documentElement.classList.remove('dark');
      localStorage.setItem('dark-mode', false);
    }
  });
});

This code remembers user preferences and updates the theme.

4. Style for dark mode

Use the dark: prefix in your HTML:

<div class="bg-white dark:bg-gray-900 text-black dark:text-white">
  <!-- Content goes here -->
</div>

That's it! You've now got a working dark mode toggle for your Tailwind site.

Related posts

Read more

Built on Unicorn Platform