Building with AI

Styling at Speed: Tailwind v4 With an Agent

Tailwind v4 moved config into CSS, and that made an agent a far better collaborator. How design tokens in one @theme block kept the whole site consistent.

Val OvinnikovVal Ovinnikov3 min read
A painter's palette of glowing design tokens — colours, spacing, type — flowing as light onto a clean web layout in warm amber-teal tones.

Styling is where AI collaboration usually falls apart. Ask an agent to "make it look nicer" and you get a snowstorm of arbitrary values — mt-[13px] here, a hex code there — that drift a little more with every edit. Tailwind v4 changed that for me by moving configuration into CSS, and a shared set of tokens turned the agent from a source of drift into a source of consistency. Part of the Building with AI series.

Config moved into CSS

Tailwind v4 is CSS-first. Instead of a JavaScript config file, the design system is declared in the stylesheet with an @theme block, and every token there becomes a utility class automatically. One import, one place to define the vocabulary:

apps/web/src/app/globals.css
1@import "tailwindcss";
2
3@theme {
4  --color-ink: #0b1f26;
5  --color-surface: #fbf7ef;
6  --color-accent: #e08a4e;
7  --font-sans: "Inter", system-ui, sans-serif;
8  --spacing-gutter: 1.5rem;
9}

That highlighted --color-ink line isn't just a variable — it mints bg-ink, text-ink, and border-ink for free. The token is the API, so there's a single, obvious name for "the main text colour" and no reason for anyone, human or agent, to reach for a raw hex.

Tokens are guardrails the agent respects

Here's the shift that mattered: a named token is a rule the agent can follow, while an empty canvas is a rule it can't. When I ask for a callout styled to match the site, it reaches for bg-surface text-ink because those exist and mean something — not bg-[#fbf7ef], which would work today and rot tomorrow.

A design token is a decision with a name. Give the agent names and it stops inventing decisions.

The CLAUDE.md rule is simply: style with tokens, never arbitrary values. Combined with the theme block, that keeps forty components speaking one visual language even though a dozen of them were first drafted by an agent.

Components stay declarative

Because the tokens carry the design, the components stay thin and readable — the class list is the spec, so a review diff shows intent, not magic numbers:

apps/web/src/components/Card.tsx -->
1export function Card({ title, excerpt }: { title: string; excerpt: string }) {
2  return (
3    <article className="bg-surface text-ink rounded-2xl p-gutter shadow-sm">
4      <h3 className="text-xl font-semibold">{title}</h3>
5      <p className="mt-2 opacity-80">{excerpt}</p>
6    </article>
7  );
8}

Changing the whole look is one block

The real dividend came the day I wanted the site warmer. I didn't hunt through components — I edited three lines in @theme, and every card, quote, and code caption moved together. That's only possible because nothing hard-coded a colour; everything referenced a token.

This is the same principle as the rest of the build: put the source of truth in one place and let everything downstream derive from it. The schema did it for content, the service layer did it for data, and @theme does it for design. An agent working against a single source of truth is fast and safe; an agent improvising against a blank file is neither.

Give styling a vocabulary and the agent stops guessing. Tokens turned "make it look nicer" from a drift generator into a one-word instruction with a predictable result. Next, the trickier visual problem I couldn't tokenize my way out of — Shipping Heroes Without a Designer.

3 min

This Blog Was a Weekend Idea

How an idea over coffee became a production blog in 24 days and ~1,250 commits, built mostly by AI agents — with a map to every post that tells the story.

building with ai
3 min

Statically Rendered, Instantly Fresh

Static pages that update the moment I publish. How a Sanity webhook triggers on-demand revalidation so a fully static Next.js site never serves stale content.

building with ai