# tl;dr
Tailwind CSS 4 ships a Rust-powered engine, config-in-CSS via @theme, container queries as first-class utilities, and native cascade layers. Install via pnpm add tailwindcss @tailwindcss/vite for Vite projects, or @tailwindcss/postcss for Next.js and Astro. The v3 tailwind.config.js file still works via the @config directive during migration.
Why utility-first still wins in 2026.
The utility-first debate is settled by usage. Tailwind is on 500,000+ public GitHub repositories and shipped by Netflix, Shopify, GitHub, and Vercel. The reasons it stuck: no naming exhaustion (you never have to invent a name for .hero-inner-wrap again), no CSS-in-JS runtime cost, no dead code (the compiler drops every unused utility), and no context switch between markup and stylesheet.
The critics are right about one thing: raw Tailwind markup gets noisy fast. The fix is extraction. Use @apply for load-bearing components (a button, a card, a form field), keep utilities inline for everything else. React and Vue component boundaries do the same job of scoping the noise.
Install Tailwind CSS 4.
For Vite:
pnpm add tailwindcss @tailwindcss/vite
Then in vite.config.ts:
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})
And a one-line entry stylesheet, src/main.css:
@import "tailwindcss";
Config in CSS with @theme.
The tailwind.config.js file is now optional. Everything you would have put in theme.extend can live in a @theme block:
@import "tailwindcss";
@theme {
--color-ink: #0B0E14;
--color-spark: #5CE1E6;
--color-amber: #F4B860;
--font-display: "Inter Tight", "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--spacing-96: 96px;
}
Now bg-ink, text-spark, font-display, font-mono, and p-96 are all valid utilities. The CSS variables are also emitted, so you can use them from raw CSS or another framework’s style block.
Arbitrary values.
When the design system runs out, drop into square-bracket notation:
<div class="w-[42.5%] mt-[calc(100vh-64px)] bg-[#0B0E14/0.85]">
arbitrary sizing, calc, opacity
</div>
Dark mode.
<div class="bg-white text-graphite dark:bg-ink dark:text-ivory">
respects prefers-color-scheme by default
</div>
For manual toggle via <html class="dark">, add @variant dark (.dark &); to your stylesheet.
Container queries.
<div class="@container">
<div class="grid grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3">
responds to the container, not the viewport
</div>
</div>
# related · monorepos
New to Turborepo?
Start with the complete guide: pnpm workspaces, turbo.json, remote caching, and a worked Next.js + Astro monorepo. Real build-time numbers throughout.
Common pitfalls.
- Purging based on globs that miss
.astroor.vuefiles. Utilities silently disappear in production. Fix: use the auto-detection in Tailwind 4, or setcontentexplicitly. - Using
@applyfor everything. Kills the point. Reserve it for four or five real components. - Fighting the design system by writing
class="text-[16px]"for every font size. Extend the theme instead. - Importing Tailwind twice (once in JS, once in CSS). Doubles bundle size.
FAQ.
Is Tailwind slow?
No. Tailwind 4’s Rust engine is 5 to 10 times faster than v3’s PostCSS pipeline. First build is under 100ms on the reference project; incremental rebuilds are 8 to 20ms.
Should I use daisyUI, shadcn/ui, or headless UI?
shadcn/ui is the current default for React projects: you copy the component source into your codebase, so there is no vendor dependency. daisyUI is Vue-and-vanilla friendly. Headless UI (from the Tailwind team) is the primitive layer both are built on.