C.W.K.
Stream
Lesson 05 of 07 · published

Tailwind v4 — The CSS-First Setup

~15 min · tailwind, v4, theme, css-variables

Level 0React Novice
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
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.

One source of truth. Don't define --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.

Code

src/index.css — full v4 setup with theme tokens and dark mode·css
@import "tailwindcss";

@theme {
  /* Dark mode is the default (no [data-theme] attribute) */
  --color-bg: #0d0d12;
  --color-bg-elevated: #16161e;
  --color-fg: #e8e8ee;
  --color-muted: #9b9bab;

  --color-brand: #FF8FBE;
  --color-brand-strong: #d62e84;

  --font-sans: "Inter", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  --radius-card: 0.75rem;
}

/* Light mode: swap a few variables when [data-theme='light'] is set */
[data-theme="light"] {
  --color-bg: #ffffff;
  --color-fg: #1a1a1f;
  --color-bg-elevated: #fafafd;
}

/* Now `bg-brand`, `text-fg`, `font-mono`, `rounded-card` all just work. */
html, body {
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: var(--font-sans);
}
A component using the theme tokens via utilities·tsx
// Every class below maps to a variable in @theme above.
export function ConversationCard({ title }: { title: string }) {
  return (
    <article className="bg-bg-elevated text-fg font-sans rounded-card p-4 shadow-sm hover:shadow-md transition-shadow">
      <h3 className="font-medium text-brand">{title}</h3>
      <p className="text-sm text-muted mt-1">Click to open</p>
    </article>
  );
}

External links

Exercise

In your bootstrap project, replace src/index.css with the v4 setup above. Add two custom colors (--color-success and --color-warning) and prove they work by writing bg-success and text-warning on visible elements. Then add a button at the top of App.tsx that toggles a data-theme attribute on document.documentElement between absent and "light" — confirm the page background flips.
Hint
For the toggle: document.documentElement.setAttribute('data-theme', current === 'light' ? '' : 'light'). Then use useState to track the current theme. We'll formalize this pattern in Track 4 lesson 2.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.