Build1 distinct publisher3 min readUpdated
Dropping tailwind.config.js for a CSS @theme block buys you tokens that any tool can read. It also means renaming every token in a shared design system to fit Tailwind's namespaces.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Tailwind CSS v4 dropped `tailwind.config.js` in favour of a plain CSS file [1]. On a one-person marketing build that is an afternoon of tidying; on a shared design system it is a rename of everything you own, because in v4 a token's name is its public API.
The mechanism fits on one screen. You import Tailwind, open an `@theme` block, and anything declared inside it becomes a utility automatically, with no config file and no plugin [3]. The same declarations remain available to hand-written CSS through `var(--color-brand-500)` for cases a utility will not reach [4]. The starting file in the dev.to writeup by Nayan Kyada holds ten declarations: four colours in `oklch`, two font stacks, two spacing values, a radius and a shadow [5][6][1].
The mapping is mechanical and unforgiving. `--color-brand-500` yields `bg-brand-500`, `--radius-card` yields `rounded-card`, `--shadow-card` yields `shadow-card` [3][2]. The namespace prefix selects which family of utilities gets generated, so if your existing tokens are exported from a JS object or a Figma variables file under names like `brand.primary`, the migration is not a paste. Every name has to be restated in the form Tailwind will parse, and the design team has to accept that the CSS name and the utility name are the same decision made once.
The escape hatch is visible even in the author's own setup. Two of the ten declarations, `--spacing-container` and `--spacing-section`, are not consumed as generated utilities at all; the `Section` wrapper reaches them with `max-w-[var(--spacing-container)]` and `py-[var(--spacing-section)]` [7][3]. That is 80rem of container and 5rem of vertical rhythm, per the file's own comments [6]. So "no config file, no plugin" still leaves arbitrary-value brackets in the markup for tokens that do not land cleanly in a utility family.
Fonts show the other seam. `--font-sans` is declared in the `@theme` block as `"Inter", ui-sans-serif, system-ui, sans-serif` [6], and then declared again at runtime by `next/font/google`, which is configured with `variable: "--font-sans"` and `display: "swap"` specifically so Tailwind picks up the loaded face [8][4]. The handoff is deliberate, but it means the token file is not the final word on that value; the framework supplies it.
The payoff, as argued in the piece, is that tokens sit in one place that the browser, Figma token plugins and a CMS preview can all read [2]. Worth noting what the article actually shows: the token file and the Next.js code around it, not a working Figma or CMS pipeline [9]. The rest of the workflow is conventional and cheap to copy: `create-next-app` with `--typescript --tailwind --app --turbopack` [10], `globals.css` stripped back to the import line before the `@theme` block goes in [11], a `cn` helper built from `clsx` and `tailwind-merge` to stop override collisions [12], and a claimed sub-200ms Turbopack HMR on component edits [13].
What to watch on a real migration: the tokens with no obvious home among the prefixes shown here, such as motion durations or stacking layers. The count of `var(--...)` strings inside class attributes is the honest metric. If it climbs, the naming did not survive the move, and you have swapped a config file for a second, less searchable one.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
Anything inside the @theme block becomes a Tailwind utility automatically (bg-brand-500, text-brand-600, rounded-card, shadow-card), with no config file and no plugin.
The author's starting file is app/globals.css containing @import "tailwindcss"; followed by an @theme block.
The @theme block declares --color-brand-500: oklch(55% 0.22 250), --color-brand-600: oklch(48% 0.22 250), --color-surface: oklch(98% 0 0), --color-surface-muted: oklch(95% 0 0), --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, --font-display: "Cal Sans", var(--font-sans), --spacing-section: 5rem (commented 80px), --spacing-container: 80rem (commented 1280px), --radius-card: 0.75rem, and --shadow-card with two oklch shadow layers.
The Section wrapper component applies the classes "mx-auto w-full max-w-[var(--spacing-container)] px-4 py-[var(--spacing-section)] sm:px-6 lg:px-8", so individual blocks never hardcode vertical rhythm.
Inter is loaded via next/font/google in the root layout with variable: "--font-sans" and display: "swap", exposed as a CSS variable so Tailwind can pick it up.
Tailwind CSS v4 dropped tailwind.config.js in favour of a plain CSS file.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Code shown, interop and speed claims unshown
Most structural claims are backed by verbatim code in the source: the full @theme block, the create-next-app command with flags, layout.tsx font wiring and section.tsx with its exact class string. That is unusually checkable for a workflow post. What pulls the score down is that the two claims doing the persuasive work — cross-tool token readability and sub-200 ms HMR — have no artefact behind them, and there is exactly one publisher with no independent corroboration.
No adoption signal in supplied material
The single source is one practitioner describing personal client workflow. It contains no release, deployment, benchmark, usage-disclosure, pricing or licensing event, no download or install figures for Tailwind v4, and no third party attesting to use of this pattern. Nothing in the supplied material supports an adoption measurement, so none is inferred.
Reach of the token file overstated
Positive but moderate. The concrete engineering is real and shown, so this is not empty hype. The overstatement is specific: 'every tool — the browser, Figma tokens plugins, your CMS preview — can read' is presented as a settled property of the approach while the article never touches Figma or a CMS, and the sub-200 ms HMR and four-hour page figures are asserted as measurements without being measured. The unmentioned cost points the same direction: making the token name the API means an existing shared design system must be renamed into Tailwind's namespaces to get any utilities at all.
Contractor showcasing a billable workflow
The source is self-published on a developer community platform and explicitly frames the content as 'the exact Next.js Tailwind workflow I use across client projects', with the payoff stated in delivery terms — a five-section page in about four hours, cheap client colour changes, easy handoff to a second contractor. That is a credible reputational and business-development incentive to present the setup as frictionless, which is consistent with the unexamined interop and timing claims. There is no disclosed vendor sponsorship, and no relationship to Tailwind Labs or Vercel is claimed, so this is ordinary practitioner self-promotion rather than a commercial placement.
Verifiable code, single unbenchmarked voice
Confidence is held down by structure rather than content quality: one publisher, one author, no corroborating source and no adoption dimension to triangulate against. It is not lower because the mechanical claims are directly inspectable from published code, and the derived findings — prefix-stripped utility naming, spacing tokens reached only via var(), the duplicated --font-sans — follow from the source's own artefacts rather than from outside assumption.
build
Next.js's architecture is working. Its operating manual isn't keeping up.1 distinct publisher
build
At 70,000 images, next/image is not your optimizer, it is your outage1 distinct publisher
build
A build step instead of a backend: 1,025 records, 8 locales, no runtime API1 distinct publisher
build
Two Next.js apps instead of one, because isAdmin is a privilege escalation waiting to happen1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 15, 2026