Implement Theme System With Dark/Light Modes
Description
In this article, we will explore how to build a theme system with dark/light mode support using Tailwind CSS. We will use Tailwind's dark mode feature and store the theme preference in local storage using Zustand.
Acceptance Criteria
To ensure that our theme system meets the requirements, we will implement the following acceptance criteria:
- Users can toggle between dark and light modes.
- Theme persists across page reloads.
Technical Requirements
To achieve the theme system with dark/light mode support, we will use the following technical requirements:
- Use Tailwind's dark mode feature (dark: prefix).
- Store theme preference in local storage via Zustand.
Prerequisites
Before we begin, make sure you have the following dependencies installed:
- Tailwind CSS
- Zustand
Step 1: Set up Tailwind CSS
To use Tailwind's dark mode feature, we need to set up Tailwind CSS in our project. We will use the @tailwindcss/forms
and @tailwindcss/typography
plugins to enable the dark mode feature.
npm install @tailwindcss/forms @tailwindcss/typography
Step 2: Configure Tailwind CSS
Next, we need to configure Tailwind CSS to use the dark mode feature. We will add the following configuration to our tailwind.config.js
file:
module.exports = {
mode: 'jit',
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
theme: {
extend: {
colors: {
dark: '#1A1D23',
light: '#F7F7F7',
},
},
},
variants: {
extend: {
backgroundColor: ['dark'],
},
},
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
};
Step 3: Create a Theme Store
To store the theme preference in local storage, we will use Zustand. We will create a theme store that will handle the theme preference.
import create from 'zustand';
const useThemeStore = create(set => ({
theme: 'light',
toggleTheme: () => {
set(state => ({ theme: state.theme === 'light' ? 'dark' : 'light' }));
},
getTheme: () => state.theme,
}));
export default useThemeStore;
Step 4: Implement Theme Toggle
Now that we have the theme store set up, we can implement the theme toggle functionality. We will create a button that will toggle the theme when clicked.
import useThemeStore from './useThemeStore';
const ThemeToggle = () => {
const { toggleTheme, getTheme } = useThemeStore();
return (
<button
className={`bg-${getTheme()}-500 hover:bg-${getTheme()}-700 text-white font-bold py-2 px-4 rounded`}
onClick={toggleTheme}
>
Toggle Theme
</button>
);
};
export default ThemeToggle;
Step 5: Persist Theme Across Page Reloads
To persist the theme across page reloads, we will use the localStorage
API to store the theme preference.
import useThemeStore from './useThemeStore';
const useThemeStore = create(set => ({
theme: localStorage.getItem('theme') || 'light',
toggleTheme: () => {
set(state => ({ theme: state.theme === 'light' ? 'dark' : 'light' }));
localStorage.setItem('theme', state.theme);
},
getTheme: () => state.theme,
}));
Conclusion
In this article, we have implemented a theme system with dark/light mode support using Tailwind CSS. We have used Tailwind's dark mode feature and stored the theme preference in local storage using Zustand. We have also implemented the theme toggle functionality and persisted the theme across page reloads.
Example Use Case
Here is an example use case of the theme system:
import React from 'react';
import ThemeToggle from './ThemeToggle';
const App = () => {
return (
<div className="bg-gray-100 h-screen">
<ThemeToggle />
<div className={`text-${useThemeStore((state) => state.theme)}-500`}>
This is a dark mode example
</div>
</div>
);
};
export default App;
Commit Message
Here is an example commit message for this feature:
feat: implement theme system with dark/light modes
API Documentation
Here is an example API documentation for the theme store:
/**
* Theme store API
*
* @module theme-store
*/
/**
* Get the current theme
*
* @returns {string} The current theme (light or dark)
*/
export const getTheme = () => {
return useThemeStore((state) => state.theme);
};
/**
* Toggle the theme
*
* @returns {void}
*/
export const toggleTheme = () => {
useThemeStore((set) => set({ theme: getTheme() === 'light' ? 'dark' : 'light' }));
};
Testing
Here is an example test for the theme toggle functionality:
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import ThemeToggle from './ThemeToggle';
describe('ThemeToggle', () => {
it('toggles the theme', async () => {
const { getByText } = render(<ThemeToggle />);
const toggleButton = getByText('Toggle Theme');
fireEvent.click(toggleButton);
await waitFor(() => {
expect(getByText('Toggle Theme')).toHaveClass('bg-dark-500');
});
});
});
```<br/>
**Theme System with Dark/Light Modes: Q&A**
=============================================
**Q: What is a theme system?**
---------------------------
A: A theme system is a way to customize the visual appearance of an application or website. It allows users to switch between different themes, such as light and dark modes, to suit their preferences.
**Q: Why is a theme system important?**
--------------------------------------
A: A theme system is important because it provides users with a way to customize the appearance of an application or website to their liking. This can improve user experience and engagement, as users are more likely to use an application or website that they can customize to their preferences.
**Q: What is Tailwind CSS?**
-------------------------
A: Tailwind CSS is a utility-first CSS framework that allows developers to write more efficient and maintainable CSS code. It provides a set of pre-defined classes that can be used to style HTML elements, making it easier to create responsive and customizable UI components.
**Q: How does Tailwind CSS support dark mode?**
--------------------------------------------
A: Tailwind CSS supports dark mode through its `dark` prefix, which can be used to apply dark mode styles to HTML elements. For example, the `dark:bg-gray-800` class can be used to apply a dark gray background color to an element.
**Q: How do I store theme preferences in local storage?**
---------------------------------------------------
A: To store theme preferences in local storage, you can use the `localStorage` API to store the theme preference as a string. For example, you can store the theme preference as a string like `localStorage.setItem('theme', 'dark')`.
**Q: How do I persist theme across page reloads?**
------------------------------------------------
A: To persist theme across page reloads, you can use the `localStorage` API to store the theme preference as a string, and then retrieve it on page load. For example, you can use the following code to persist theme across page reloads:
```javascript
import useThemeStore from './useThemeStore';
const useThemeStore = create(set => ({
theme: localStorage.getItem('theme') || 'light',
toggleTheme: () => {
set(state => ({ theme: state.theme === 'light' ? 'dark' : 'light' }));
localStorage.setItem('theme', state.theme);
},
getTheme: () => state.theme,
}));
Q: How do I implement theme toggle functionality?
A: To implement theme toggle functionality, you can create a button that toggles the theme when clicked. For example, you can use the following code to implement theme toggle functionality:
import useThemeStore from './useThemeStore';
const ThemeToggle = () => {
const { toggleTheme, getTheme } = useThemeStore();
return (
<button
className={`bg-${getTheme()}-500 hover:bg-${getTheme()}-700 text-white font-bold py-2 px-4 rounded`}
onClick={toggleTheme}
>
Toggle Theme
</button>
);
};
export default ThemeToggle;
Q: How do I test theme toggle functionality?
A: To test theme toggle functionality, you can use a testing library like Jest or Cypress to write unit tests or integration tests for the theme toggle functionality. For example, you can use the following code to test theme toggle functionality:
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import ThemeToggle from './ThemeToggle';
describe('ThemeToggle', () => {
it('toggles the theme', async () => {
const { getByText } = render(<ThemeToggle />);
const toggleButton = getByText('Toggle Theme');
fireEvent.click(toggleButton);
await waitFor(() => {
expect(getByText('Toggle Theme')).toHaveClass('bg-dark-500');
});
});
});
Q: What are some best practices for implementing a theme system?
A: Some best practices for implementing a theme system include:
- Using a utility-first CSS framework like Tailwind CSS to make it easier to create responsive and customizable UI components.
- Storing theme preferences in local storage to persist theme across page reloads.
- Implementing theme toggle functionality to allow users to switch between different themes.
- Testing theme toggle functionality to ensure it works correctly.
- Using a consistent naming convention for theme-related variables and functions.
- Documenting theme-related code to make it easier for other developers to understand and maintain.