Theme Configuration in CSS

Our project is already setup with Tailwind CSS v4.
If you're using vscode, you might want to upgrade the Tailwind CSS Intellisense extension to the "pre-release" version to work with Tailwind v4.

Tailwind CSS v4 upgrade

  1. We've upgraded our Tailwind version and installed the new Tailwind CSS Vite plugin:
"@tailwindcss/vite": "^4.0.0-alpha.17",
"tailwindcss": "^4.0.0-alpha.17",
  1. We've registered the Vite plugin in :
import tailwindVite from '@tailwindcss/vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [react(), tailwindVite()],
	server: {
		port: Number(process.env.PORT) || 3000,
	},
})
  1. We've replaced the three @tailwind directives in with a single @import statement:
- @tailwind base;
- @tailwind components;
- @tailwind utilities;
+ @import 'tailwindcss';
  1. We've deleted the postcss.config.js file, which is no longer needed.
We could also delete thetailwind.config.ts file, but we're keeping it around for reference of the work we've done.

Porting the theme configuration to CSS

Instead of defining the backgroundColor, borderColor, and textColor objects in the tailwind.config.ts file, you're going to register those in CSS directly.
The GREAT news is that Tailwind CSS v4 uses CSS Variables as the primary theme configuration mechanism β€”Β so a lot of our gymnastics between CSS and JS configuration is going to be greatly simplified.
The equivalent of Tailwind config's theme.extend in v4 is an @theme block in CSS.
The various theme core plugins can be extended (or overriden) by defining CSS variables with a specific naming convention.
Here is a complete list of the CSS variable names for the @theme definition.
The Tailwind Intellisense extension should give you autocomplete suggestions as you start typing -- inside the @theme block.

1. Move semantic token definition to the @theme block

Keep the raw color tokens in the @layer base for now (we'll address this later).
Take the semantic tokens CSS variables in your CSS file, and move theme outside of the @layer base, and into a new @theme block:
@layer base {
	:root {
		/* raw color tokens (`--color-grey-0`, etc ) */
	}
}

@theme {
	--background-color-neutral: var(--color-grey-0);
	/* ... */
}
Note that the CSS variable name hase changed from --color-bg-neutral to --background-color-neutral! This is to accomodate for the @theme naming convention.
Here's a list of the tokens you need to move:
/* Semantic tokens */
--color-bg-highlight: var(--color-teal);
--color-bg-accent: var(--color-purple);

--color-bg-neutral: var(--color-grey-0);
--color-bg-neutral-inverted: var(--color-grey-100);
--color-bg-subtle: var(--color-grey-5);
--color-bg-bold: var(--color-grey-80);

--color-border-bold: var(--color-grey-60);
--color-border-subtle: var(--color-grey-40);
--color-border-muted: var(--color-grey-20);

--color-text-copy: var(--color-grey-100);
--color-text-subtle: var(--color-grey-60);
--color-text-muted: var(--color-grey-40);
--color-text-inverted: var(--color-grey-5);
Make sure all the semantic color tokens are moved to the @theme and re-named accordingly.
Warning: Doing this will not _replace the default Tailwind colors, but rather extend them.
Remember: we want to override the default theme colors. Here's the way to do this in the @theme:
@theme {
	--color-*: initial;
}
  • color-* targets all color variables in the theme.
  • initial clears those variables.
Effectively the equivalent of doing theme.colors = {} in the Tailiwnd config.

2. Rename dark and theme CSS variables to mirror the @theme CSS variables

The @theme variables become the new :root scope CSS variables for our project. Anywhere you redefine those variables (dark, data-theme-"citrus") needs to be updated to reflect the new naming convention.

3. Use the full color syntax, not H S L channels

All the complexity of composing a color with Tailwind's internal <alpha-value> goes away. You can just pass the full color (HSL` in our case) to the CSS variable.
Update all CSS variables that use H S L channels to use the full HSL color syntax:
@layer base {
	:root {
-		--color-teal: 173 100% 50%;
+		--color-teal: hsl(173 100% 50%);
	}
}
If you've done everything right β€”Β the UI should once again have the correct colors for both themes, in both light and dark modes.

No tests here 😒 Sorry.