✳ Nairobi, Kenya
- WhatsApp+254 799 756 331
- Emailhello@muvevi.com
- LinkedInlinkedin.com/in/muvevi
- GitHubgithub.com/mutua-muvevi
- Twitter / X@muvevi
Available for freelance & full-time roles.
Let's build something great.
Tailwind v4 in production
— what actually changed
Lightning CSS, CSS variables everywhere, and no config file. After shipping two production apps with Tailwind v4, here's the honest review.
Tailwind v4 is a full rewrite, not an incremental update, and the mental model shift is larger than the changelog suggests.
The most significant change is that the framework now generates CSS custom properties by default for every design token. Colors, spacing, fonts — all become CSS variables at the :root level. This means you can read and override any design token from plain CSS or from JavaScript, without a PostCSS plugin or theme extension.
/* v3 — values compiled away at build time */
.text-blue-500 { color: #3b82f6; }
/* v4 — values are live custom properties */
:root { --color-blue-500: oklch(62.3% 0.214 259.8); }
.text-blue-500 { color: var(--color-blue-500); }The config file is gone. In v4, configuration lives in CSS itself using the @theme directive. This feels odd at first — you're used to a JavaScript object with type-safe keys. In practice, it's a better separation: your config is in the same layer as your styles, not in a side-channel JS file.
@theme {
--font-display: "Patua One", sans-serif;
--color-brand: oklch(65% 0.2 300);
--radius-btn: 0.375rem;
}Lightning CSS. Tailwind v4 ships with Lightning CSS as the bundler and transformer. You get nesting, oklch(), color-mix(), and modern range queries compiled for you without configuring PostCSS at all. For Next.js, this required switching from the postcss plugin to the new @tailwindcss/vite (or @tailwindcss/next for App Router). The migration is one dependency swap and a CSS import change.
What didn't land as smoothly: the utility purging. In v3, you control the content glob explicitly. In v4, it auto-detects, but it's not perfect. In one of my apps, utility classes inside dynamically-built class strings (from a Zustand store) were being purged. The fix is @source directives in your CSS — explicit opt-ins for directories that may contain dynamic class strings.
After two production deployments, the bundle is visibly smaller (20-30% in my cases), the DX is faster (Lightning CSS is quick), and the custom property system makes runtime theming — dark/light, user-chosen accent colors — genuinely easy without JS-in-CSS hacks.