I am using TailwindCSS in my React + Vite project, and the classes are loaded correctly. The UI looks complete.
The problem is that colors from variables defined in tailwind.config.js are not applied.
I'm using variables like foreground, background, primary, and secondary, but I don't see any colors in the browser.
I verified in DevTools that the classes and variables are loaded.
Why aren't the colors being applied?
Here is my configuration:
tailwind.config.cjs
const plugin = require("tailwindcss/plugin");
module.exports = {
darkMode: ["class"], // use class strategy for dark mode
content: [
"./index.html", // include static HTML
"./pages/**/*.{ts,tsx,js,jsx}",
"./components/**/*.{ts,tsx,js,jsx}",
"./app/**/*.{ts,tsx,js,jsx}",
"./src/**/*.{ts,tsx,js,jsx}",
],
safelist: [
"bg-background",
"text-foreground",
"bg-card",
"text-card-foreground",
"bg-primary",
"text-primary-foreground",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
"icon-bg": "hsl(var(--icon-bg))",
"text-secondary": "hsl(var(--text-secondary))",
"progress-bg": "hsl(var(--progress-bg))",
"progress-active": "hsl(var(--progress-active))",
"chat-bg": "hsl(var(--chat-bg))",
"chat-border": "hsl(var(--chat-border))",
"chat-bot-bubble": "hsl(var(--chat-bot-bubble))",
"chat-user-bubble": "hsl(var(--chat-user-bubble))",
success: "hsl(var(--success))",
"success-bg": "hsl(var(--success-bg))",
sidebar: {
DEFAULT: "hsl(var(--sidebar-background))",
foreground: "hsl(var(--sidebar-foreground))",
primary: "hsl(var(--sidebar-primary))",
"primary-foreground": "hsl(var(--sidebar-primary-foreground))",
accent: "hsl(var(--sidebar-accent))",
"accent-foreground": "hsl(var(--sidebar-accent-foreground))",
border: "hsl(var(--sidebar-border))",
ring: "hsl(var(--sidebar-ring))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
};
tailwind.css
@import "./styles/tokens.css";
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
tokens.css
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Definition of the design system. All colors, gradients, fonts, etc should be defined here.
All colors MUST be HSL.
*/
@layer base {
:root {
--background: 245 25% 97%;
--foreground: 222 20% 20%;
--card: 210 100% 98%;
--card-foreground: 0 0% 11%;
--popover: 0 0% 100%;
--popover-foreground: 222 20% 20%;
--primary: 249 79% 64%;
--primary-foreground: 0 0% 100%;
--secondary: 220 14% 96%;
--secondary-foreground: 222 20% 20%;
--muted: 220 14% 96%;
--muted-foreground: 215 12% 55%;
--accent: 249 79% 64%;
--accent-foreground: 0 0% 100%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 100%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--ring: 249 79% 64%;
--radius: 0.75rem;
--icon-bg: 249 100% 95%;
--text-secondary: 220 14% 58%;
--progress-bg: 220 14% 96%;
--progress-active: 249 79% 64%;
--chat-bg: 210 100% 98%;
--chat-border: 216 22% 90%;
--chat-bot-bubble: 210 100% 98%;
--chat-user-bubble: 249 100% 95%;
--success: 142 71% 45%;
--success-bg: 142 76% 96%;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--background: 222 30% 12%;
--foreground: 210 40% 98%;
--card: 222 28% 15%;
--card-foreground: 210 40% 98%;
--popover: 222 28% 15%;
--popover-foreground: 210 40% 98%;
--primary: 249 79% 64%;
--primary-foreground: 0 0% 100%;
--secondary: 217 32% 20%;
--secondary-foreground: 210 40% 98%;
--muted: 217 32% 20%;
--muted-foreground: 215 20% 65%;
--accent: 249 79% 64%;
--accent-foreground: 0 0% 100%;
--destructive: 0 62.8% 50%;
--destructive-foreground: 0 0% 100%;
--border: 217 32% 22%;
--input: 217 32% 22%;
--ring: 249 79% 64%;
--icon-bg: 249 50% 25%;
--text-secondary: 215 20% 65%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}
vite.config
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [react(),tailwindcss()],
define: {
'global': 'globalThis',
},
css: {
postcss: './postcss.config.js', // optional if you have PostCSS config
},
});
postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
};
I am using TailwindCSS version 4.1.17.
Example usage in a component:
<div dir="rtl" className="font-sans min-h-screen flex flex-col items-center justify-center p-4 bg-background relative">
-
2Hi! Could you include in your post: (a) your index.css or wherever you define your variables such as --primary and (b) an example of how you use these variables in your react component? The extra context could help with troubleshooting your project :)Talos0248– Talos02482025年11月25日 12:50:24 +00:00Commented yesterday
-
1Additionally, could you confirm the version of Tailwind your project is using — whether it is on version 4 or version 3? I believe Tailwind 4 has introduced many syntax changes, and no longer uses tailwind.config.cjsTalos0248– Talos02482025年11月25日 12:59:34 +00:00Commented 23 hours ago
-
1I added more details. I think it's not a duplicate.kobi shunak– kobi shunak2025年11月25日 14:37:33 +00:00Commented 22 hours ago
-
also I'm not sure if using v3 will solve the issue but I will try.kobi shunak– kobi shunak2025年11月25日 14:38:03 +00:00Commented 22 hours ago
2 Answers 2
New @import "tailwindcss"; instead of @tailwind
Starting from TailwindCSS v4, the @tailwind directives are no longer used; instead, a single @import is required:
Not supported from v4
@tailwind base; @tailwind components; @tailwind utilities;Supported from v4
@import "tailwindcss";
New CSS-first configuration instead of tailwind.config.js
Also, using tailwind.config.js (i.e., JS-based configuration) is no longer the default; instead, CSS-first configuration and automatic source detection have been introduced. Since there is a lot of similar content on this topic, I would rather refer to those instead of creating a duplicate:
- New CSS-first configuration option in v4 (
@theme,@custom-variant,@utility) - Automatic Source Detection from TailwindCSS v4 (instead of
contentproperty)
I'll show you what your card color declaration would look like, for example:
/* Reference: https://stackoverflow.com/a/79499827/15167500 */
@import "tailwindcss"; /* instead of @tailwind directives */
@plugin "tailwindcss-animate"; /* instead of plugins[] array */
@custom-variant dark (&:where(.dark, .dark *)); /* instead of darkMode: ["class"] */
/* Default colors (light mode) - instead of `theme.extend` */
/* Reference: https://github.com/tailwindlabs/tailwindcss/discussions/19020 (all namespaces and old-property pairs)*/
@theme {
--color-card: hsl(210 100% 98%);
--color-card-foreground: hsl(0 0% 11%);
}
/* Theme colors */
@layer theme {
:root, :host {
/* For dark mode */
@variant dark {
--color-card: hsl(222 28% 15%);
--color-card-foreground: hsl(210 40% 98%);
}
}
}
As you can see, there is no longer a need for CSS variables that you then reference in a JS configuration. Instead, colors should be declared in @theme. TailwindCSS automatically creates a global variable for these, which can later be overridden with CSS, for example, for dark mode. For manually dark mode, you also need to override the variant selector using the new special @custom-variant directive.
References:
- How to use custom color themes in TailwindCSS v4 -
@layer theme&@variant dark - How to override theme variables in TailwindCSS v4 -
@themevs@layer themevs:root - Should I use
@themeor@theme inline? - When should I use
*and when should I use:root, :hostas the parent selector? - Which TailwindCSS v4 namespace matches a given TailwindCSS v3's theme keys?
- Tailwind CSS v4 - Unknown at rule @plugin, @custom-variant, @theme, @utility, @variant, @apply, @source, @reference in global.css
- Safelist from TailwindCSS v4
PostCSS-plugin for TailwindCSS v4
For PostCSS, it is recommended to use postcss.config.mjs instead of .js. Furthermore, there is no need for Autoprefixer, as v4 already uses LightningCSS under the hood to perform the necessary adjustments and minification for the Baseline 2023 browsers (Chrome 111, Firefox 128, Safari 16.4) it supports.
References:
New Vite-plugin for TailwindCSS v4
For Vite, you can use a special plugin starting from v4, without PostCSS:
Comments
TL;DWR: I believe you're using Tailwind 3 syntax in Tailwind 4. You'll have to switch to using Tailwind 4 syntax if you intend to keep on using Tailwind version 4 (which I personally recommend using, since Tailwind 4's approach feels more like regular CSS and is easier to pick up, IMO).
I think I see what's going on — your vite.config seems to be set up correctly (for Tailwind), but you're using Tailwind 3 syntax in Tailwind 4. Here's a quick guide on getting your code working again! Do note the comments in the following code snippets; I've left them there instead of breaking my explanation into chunks for (hopefully) easier copying!
App.css
(You may also choose a different CSS file, just make sure to import it)
/*The following line the new way of importing tailwind.
Make sure this CSS file is imported somewhere in your project
(e.g. in App.tsx) */
@import "tailwindcss";
/* Use theme directives to include your customizations */
@theme {
/* Notice the 'color' prefix!
This is necessary: if you want to use 'bg-background' in your code,
you MUST name the variable '--color-background' instead of just '--background'.
The same logic applies to similar cases:
E.g. You MUST use --color-foreground, --color-card for text-foreground, border-card, etc.:
See: https://tailwindcss.com/docs/theme
*/
--color-background: hsl(245 25% 97%); /* Notice that hsl() is directly used here */
--color-foreground: hsl(222 20% 20%);
/* You may do the same for the rest of your custom properties below*/
}
App.tsx
(Example usage)
import './App.css' // Notice that this (i.e., whichever CSS file you are using) MUST be imported!
// (You only have to import it ONCE, no need to reimport in all files which use tailwind themes)
function App() {
return (
<>
<div>
<div dir="rtl" className="font-sans text-4xl min-h-screen flex flex-col items-center justify-center p-4 bg-background relative">
<p className="text-foreground">Hello there!</p>
{/*You can also use your theme variables as regular css variables:*/}
<p style={{ color:"var(--color-foreground)" }}> I'm using CSS variables!!!</p>
</div>
</div>
</>
)
}
export default App
Sample outcome:
Sample outcome of the above code
Once you're done with your refactor, tailwind.config.cjs, tailwind.css, and tokens.css can (probably) be safely deleted (though I would recommend making a backup, both AFTER (but pre-delete) and BEFORE refactoring, just in case!)
Hope this helps!