v3 → v4 is the biggest shift Tailwind has shipped. The JS config file is gone for most projects. Your design tokens live in CSS now, where they always belonged.
What changed
In v3 your theme lived in tailwind.config.js as a JavaScript object. Tailwind read it, generated CSS variables internally, and emitted utilities. You couldn't easily reference your theme tokens from your own CSS.
In v4 your theme lives in CSS as @theme declarations. Tailwind reads them as plain CSS variables. The utilities work the same way (bg-brand reads --color-brand). The difference is that your CSS can read them too. No bridge layer.
The @theme block
Inside @theme you declare CSS custom properties. Tailwind reserves a small number of prefixes that map to utility families:
--color-*→bg-*,text-*,border-*,ring-*--font-*→font-*--spacing-*→p-*,m-*,gap-*, etc.--radius-*→rounded-*--shadow-*→shadow-*--breakpoint-*→sm:,md:, etc.
Declare a token, the utility appears. No restart, no compile step you have to remember.
The cwkPippa pattern
cwkPippa's frontend/src/index.css uses the v4 model. Dark mode is keyed off a [data-theme='light'] selector (default dark, lighten on attribute switch). Font tokens map to Inter for sans and JetBrains Mono for code. The accent color is Pippa-pink because of course it is.
Customizing without ejecting
You can still escape to a config file if you need a JavaScript plugin (e.g. a custom variant). But for design tokens — colors, fonts, spacing, breakpoints — pure CSS is the path.
--color-brand in @theme AND a separate :root block AND a SCSS variable elsewhere. Pick one home. The point of v4 is that @theme is that home — everything flows from there.Migration note for v3 users
Existing v3 projects don't have to flip immediately. v3 syntax (@tailwind base; etc.) still works in v4 for compatibility. But new projects, new lessons, new examples should be v4-native. This quest is v4-native end-to-end.