Why Gwan Moved to Tailwind CSS v4

Gwan was built on Tailwind v3. When v4 landed in early 2025 with a completely redesigned configuration model, I rewrote the theme setup. This post covers what changed, why the new approach is strictly better for a component library, and what you need to know to use Gwan with Tailwind v4.
What changed in v4
1. No more tailwind.config.js for theming
In v3, your colour tokens lived in tailwind.config.js:
// v3
module.exports = {
theme: {
extend: {
colors: {
primary: { 500: "#9ea593" },
},
},
},
};In v4, all theme tokens move to CSS inside an @theme block:
/* v4 */
@theme {
--color-primary-500: #9ea593;
}Tailwind reads this at build time and generates the utility classes from it. Same result, but now your design tokens are in CSS — where they belong.
2. CSS custom properties everywhere
Every @theme value becomes a CSS custom property at runtime. This is the key change for dark mode: you can override @theme values with CSS variable overrides inside .dark {} without any JavaScript involvement.
:root {
--primary-default: #435240;
}
.dark {
--primary-default: #adc09e;
}
@theme {
--color-primary-default: var(--primary-default);
}In v3, dark mode required the dark: prefix on every utility. In v4, semantic tokens just are the right value for the current theme.
3. @import "tailwindcss" replaces directives
v3 required three directives in your CSS:
/* v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;v4 collapses these to one import:
/* v4 */
@import "tailwindcss";4. Plugins use @plugin
/* v4 */
@plugin "@tailwindcss/typography";Instead of adding to the plugins array in config.
5. Content scanning is mostly automatic
In v4, Tailwind scans your project files automatically. You only need to configure content for packages that live outside your project root — like component libraries:
// tailwind.config.ts (still needed for content paths)
content: [
"./src/**/*.{ts,tsx}",
join(dirname(require.resolve("gwan-design-system")), "**/*.{js,ts,tsx}"),
],Why this is better for a component library
In v3, library consumers had to add the library's source to their content array and extend the theme with the library's colour tokens. If they forgot either, components would render without styles or colours.
In v4, the library ships pre-built CSS that uses CSS custom properties. Consumers only need to:
- Add the library source to
content(so Tailwind scans class names) - Define the CSS variables in their
globals.css
The token values are runtime CSS, not build-time Tailwind config. This makes theming more resilient and makes the library less tightly coupled to the consumer's build setup.
Upgrading from v3
npm install tailwindcss@latest @tailwindcss/postcss@latest- Replace the three
@tailwinddirectives with@import "tailwindcss" - Move
theme.extend.colorsfrom your config to@theme {}in your CSS - Replace
plugins: [require(...)]with@plugin "..."in your CSS - Test — most utility classes are identical between v3 and v4
The official v4 upgrade guide covers edge cases.
Summary
Tailwind v4 is a better foundation for design systems because theming is pure CSS, not JavaScript config. Tokens are runtime properties that respond to class selectors, which makes dark mode and brand customisation work with zero JavaScript.
Share this article
Written by
Nimesh Fonseka