I have theme change functionality in my app using context. The theme (dark/light) class is being applied, but the colors don't change. I don't know if it's my tailwind config because when I try to type the "bg-primary" in the header, the autocomplete doesn't show the primary var, or it's the ThemeContext logic.
ThemeContext.tsx
import React, { Dispatch, SetStateAction, ReactNode } from "react";
const getInitialTheme = () => {
if (typeof window !== "undefined" && window.localStorage) {
const storedPrefs = window.localStorage.getItem("color-theme") as
| "light"
| "dark"
| null;
if (storedPrefs) {
return storedPrefs;
}
}
return "light";
};
interface ThemeContextType {
theme: "light" | "dark";
setTheme: Dispatch<SetStateAction<"light" | "dark">>;
}
export const ThemeContext = React.createContext<ThemeContextType | undefined>(
undefined,
);
export const ThemeProvider = ({ children }: { children: ReactNode }) => {
const [theme, setTheme] = React.useState<"light" | "dark">(getInitialTheme);
const rawSetTheme = (theme: "light" | "dark") => {
const root = window.document.documentElement;
const isDark = theme === "dark";
root.classList.remove(isDark ? "light" : "dark");
root.classList.add(theme);
localStorage.setItem("color-theme", theme);
};
React.useEffect(() => {
rawSetTheme(theme);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
ThemeToggler.tsx
import { ReactElement } from "react";
import { useTheme } from "../../hooks/useTheme";
import { Sun, Moon } from "lucide-react";
function ThemeToggler(): ReactElement {
const { theme, setTheme } = useTheme();
function toggleTheme() {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
}
return theme === "light" ? (
<Moon onClick={toggleTheme} />
) : (
<Sun onClick={toggleTheme} />
);
}
export default ThemeToggler;
useTheme.tsx
import { useContext } from "react";
import { ThemeContext } from "../Contexts/ThemeContext";
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme deve ser usado dentro de um ThemeProvider");
}
return context;
};
Header.tsx
import { ReactElement } from "react";
import ThemeToggler from "../ui/ThemeToggler";
function Header(): ReactElement {
return (
<header
className={
"flex h-20 w-svw items-center justify-between px-4 text-xl"
}
>
<div>
<h1>CashierApp</h1>
</div>
<div>
<nav>
<ul className="flex flex-row items-center gap-8">
<li>Home</li>
<li>Controle de caixa</li>
<li>Cadastros</li>
</ul>
</nav>
</div>
<div>
<ThemeToggler />
</div>
</header>
);
}
export default Header;
tailwind.config.ts
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{html,js,jsx,ts,tsx}"],
theme: {
extend: {
colors: {
primary: "var(--color-bg-primary)",
secondary: "var(--color-bg-secondary)",
textPrimary: "var(--color-text-primary)",
textSecondary: "var(--color-text-secondary)",
textAccent: "var(--color-text-accent)",
},
},
},
plugins: [],
};
Edit: I'm using tailwindcss v4, and my css file has the following statements:
@import "tailwindcss";
@tailwind utilities;
I have theme change functionality in my app using context. The theme (dark/light) class is being applied, but the colors don't change. I don't know if it's my tailwind config because when I try to type the "bg-primary" in the header, the autocomplete doesn't show the primary var, or it's the ThemeContext logic.
ThemeContext.tsx
import React, { Dispatch, SetStateAction, ReactNode } from "react";
const getInitialTheme = () => {
if (typeof window !== "undefined" && window.localStorage) {
const storedPrefs = window.localStorage.getItem("color-theme") as
| "light"
| "dark"
| null;
if (storedPrefs) {
return storedPrefs;
}
}
return "light";
};
interface ThemeContextType {
theme: "light" | "dark";
setTheme: Dispatch<SetStateAction<"light" | "dark">>;
}
export const ThemeContext = React.createContext<ThemeContextType | undefined>(
undefined,
);
export const ThemeProvider = ({ children }: { children: ReactNode }) => {
const [theme, setTheme] = React.useState<"light" | "dark">(getInitialTheme);
const rawSetTheme = (theme: "light" | "dark") => {
const root = window.document.documentElement;
const isDark = theme === "dark";
root.classList.remove(isDark ? "light" : "dark");
root.classList.add(theme);
localStorage.setItem("color-theme", theme);
};
React.useEffect(() => {
rawSetTheme(theme);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
ThemeToggler.tsx
import { ReactElement } from "react";
import { useTheme } from "../../hooks/useTheme";
import { Sun, Moon } from "lucide-react";
function ThemeToggler(): ReactElement {
const { theme, setTheme } = useTheme();
function toggleTheme() {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
}
return theme === "light" ? (
<Moon onClick={toggleTheme} />
) : (
<Sun onClick={toggleTheme} />
);
}
export default ThemeToggler;
useTheme.tsx
import { useContext } from "react";
import { ThemeContext } from "../Contexts/ThemeContext";
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme deve ser usado dentro de um ThemeProvider");
}
return context;
};
Header.tsx
import { ReactElement } from "react";
import ThemeToggler from "../ui/ThemeToggler";
function Header(): ReactElement {
return (
<header
className={
"flex h-20 w-svw items-center justify-between px-4 text-xl"
}
>
<div>
<h1>CashierApp</h1>
</div>
<div>
<nav>
<ul className="flex flex-row items-center gap-8">
<li>Home</li>
<li>Controle de caixa</li>
<li>Cadastros</li>
</ul>
</nav>
</div>
<div>
<ThemeToggler />
</div>
</header>
);
}
export default Header;
tailwind.config.ts
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{html,js,jsx,ts,tsx}"],
theme: {
extend: {
colors: {
primary: "var(--color-bg-primary)",
secondary: "var(--color-bg-secondary)",
textPrimary: "var(--color-text-primary)",
textSecondary: "var(--color-text-secondary)",
textAccent: "var(--color-text-accent)",
},
},
},
plugins: [],
};
Edit: I'm using tailwindcss v4, and my css file has the following statements:
@import "tailwindcss";
@tailwind utilities;
Share Improve this question edited Mar 15 at 10:59 Luckkks asked Mar 14 at 2:08 LuckkksLuckkks 231 silver badge7 bronze badges 12- What version of TailwindCSS are you using? v3 or v4? How did you implement Tailwind in the CSS? – rozsazoltan Commented Mar 14 at 11:46
- How to use custom color themes in TailwindCSS v4 or How can I implement the darkmode in my project? or How to disable dark mode in TailwindCSS 4 – rozsazoltan Commented Mar 14 at 11:49
- 1 I'll try the @theme directive, thank you – Luckkks Commented Mar 15 at 11:03
- 1 I think the example in the first link is exactly the answer to your question. There, different values needed to be set for the light and dark themes: TailwindCSS v4 dark theme by class not working without dark tag -- TailwindCSS dark mode can't change <html> or add class="dark" -- If you have any further questions, feel free to ask them in the form of a New Question and provide a similarly detailed description to reproduce the issue. – rozsazoltan Commented Mar 15 at 16:17
- 1 works perfectly, thank you for helping me – Luckkks Commented Mar 15 at 22:07
1 Answer
Reset to default 1CSS-first configuration from TailwindCSS v4
Since you're using v4, you don't need the tailwind.config.js
file; instead, you should use the CSS-first directives.
- CSS-first configuration - TailwindCSS v4 Blog
- Functions and directives - TailwindCSS v4 Docs
- New configuration option in v4 - StackOverflow
In CSS-first, you can define custom styles using the @theme
directive.
@import "tailwindcss";
@theme {
--font-display: "Satoshi", "sans-serif";
--breakpoint-3xl: 120rem;
--color-avocado-100: oklch(0.99 0 0);
--color-avocado-200: oklch(0.98 0.04 113.22);
--color-avocado-300: oklch(0.94 0.11 115.03);
--color-avocado-400: oklch(0.92 0.19 114.08);
--color-avocado-500: oklch(0.84 0.18 117.33);
--color-avocado-600: oklch(0.53 0.12 118.34);
--ease-fluid: cubic-bezier(0.3, 0, 0, 1);
--ease-snappy: cubic-bezier(0.2, 0, 0, 1);
/* ... */
}
@theme
directive - TailwindCSS v4 Docs
Theme variables are defined in namespaces and each namespace corresponds to one or more utility class or variant APIs.
- Theme variable namespaces - TailwindCSS v4 Docs
- Default theme variable reference - TailwindCSS v4 Docs
How to use legacy JavaScript based configuration
However, it is still possible to continue using the tailwind.config.js
through the @config
directive.
- TailwindCSS v4 is backwards compatible with v3 - StackOverflow
Related:
- What's changed in TailwindCSS v4?