Use when migrating Tailwind v3 to v4, configuring CSS-first @theme tokens, debugging Oxide engine errors, dealing with the absence of tailwind.config.js, container queries (@container) usage, layer cascade problems, or "dynamic class names not generating". Triggers: switching from `@tailwind base` to `@import "tailwindcss"`, RSC/Next.js streaming + CSS ordering, dark-mode strategy choices, custom variants via @variant, content-scanning glob tuning. NOT for Tailwind v3 (different config model), CSS-in-JS frameworks, vanilla CSS architecture (BEM/SMACSS), or react-native styling.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Use when migrating Tailwind v3 to v4, configuring CSS-first @theme tokens, debugging Oxide engine errors, dealing with the absence of tailwind.config.js, container queries (@container) usage, layer cascade problems, or "dynamic class names not generating". Triggers: switching from `@tailwind base` to `@import "tailwindcss"`, RSC/Next.js streaming + CSS ordering, dark-mode strategy choices, custom variants via @variant, content-scanning glob tuning. NOT for Tailwind v3 (different config model), CSS-in-JS frameworks, vanilla CSS architecture (BEM/SMACSS), or react-native styling.
metadata
{"category":"Frontend & UI","tags":["tailwind","css","design-tokens","oxide","container-queries","frontend"],"provenance":{"kind":"first-party","owners":["port-daddy"]},"pairs-with":[{"skill":"web-design-expert","reason":"Owns the design decisions (layout, hierarchy, visual voice) that this skill's @theme tokens and utilities implement."},{"skill":"color-contrast-auditor","reason":"The @theme color tokens defined here should pass its contrast checks in both light and dark variants."},{"skill":"ideal-web-app-builder","reason":"The app-scaffolding skill whose Next.js/globals.css setup this skill's v4 import model plugs into."}],"io-contract":{"kind":"deliverable","consumes":["[Truncated]","[Truncated]"],"produces":["[Truncated]","[Truncated]"]}}
Tailwind v4 Expert
Tailwind v4 is a different tool than v3. The PostCSS-based JIT engine is gone, replaced by Oxide (Rust). The JS config file is gone, replaced by CSS-first @theme blocks. Plugins from v3 mostly need rewriting. If you're migrating, expect to rebuild — not patch — your config layer.
When to use
Migrating a v3 project (look for @tailwind base; @tailwind components; @tailwind utilities;).
Need design tokens (colors, fonts, breakpoints) consumed by both Tailwind and raw CSS.
Container queries — designing components that adapt to their container, not the viewport.
Custom variants beyond what v4 ships (@variant focus-within-sibling, etc.).
Dynamic class names disappearing from the build (Oxide can't see what isn't statically present).
There is no tailwind.config.js. There is no content array. @source adds globs to the default scan root (the file's dir + project conventions). Oxide statically scans these for class strings.
These tokens generate utilities (text-brand-500, font-display, 3xl:, w-128) AND are accessible as var(--color-brand-500) in raw CSS. One source of truth.
Namespaces that auto-generate utilities:
Token prefix
Generates
--color-*
text-*, bg-*, border-*, outline-*, etc.
--font-*
font-*
--text-*
text-* (font-size; pair with --text-*--line-height)
--spacing-*
m-*, p-*, w-*, h-*, gap-*, etc.
--breakpoint-*
sm:, md:, lg:… variants
--radius-*
rounded-*
--shadow-*
shadow-*
@layer cascade
@layer base {
body { @apply font-sans text-zinc-900; }
}
@layer components {
.btn { @apply rounded-lg px-4 py-2 font-semibold; }
}
/* Custom layer between components and utilities */@layer my-overrides {
.proseimg { border-radius: var(--radius-lg); }
}
Layers cascade: base → components → utilities → arbitrary user layers. Tailwind defines its own at @layer base, components, utilities; automatically; you rarely need to redeclare them.
Put @import "tailwindcss" and @theme in app/globals.css.
Import that file once in app/layout.tsx.
For RSC streaming, ensure the CSS link is in <head> (Next does this automatically when imported from layout).
Server Components can use Tailwind classes directly; no client boundary needed.
If you ship a workspace @my-org/ui package, add @source "../../packages/ui/**/*.{ts,tsx}" to your globals so Oxide scans it.
Anti-patterns
Dynamic class names that Oxide can't see
Symptom:text-${color}-500 works in dev, missing in production.
Diagnosis: Oxide is a static scanner — it sees string literals, not template strings.
Fix: Map the dynamic value to a literal class:
Or use CSS variables for the dynamic part: style={{ '--accent': color }} class="bg-[--accent]".
Migrating but keeping tailwind.config.js
Symptom: v4 builds, but custom theme tokens don't appear; warnings about unknown directives.
Diagnosis: v4 ignores tailwind.config.js. The presence of one suggests an incomplete migration.
Fix: Move theme.extend values into @theme, plugins into @variant/CSS, content into @source. Delete the file. Run npx @tailwindcss/upgrade@latest for an automated first pass.
@apply for everything
Symptom: CSS bundle bigger than v3, "components" file is 2000 lines of @apply.
Diagnosis: Each @apply inlines utilities; deeply-applied components don't dedupe well.
Fix: Apply once at the component boundary; compose via class strings (clsx/cva) for variants.
Forgetting @source for files outside the project root
Symptom: UI library shipped from a sibling workspace — its classes don't appear in production CSS.
Diagnosis: Default scan covers the project's own files. Workspace deps need explicit @source.
Fix:@source "../../packages/ui/src/**/*.{ts,tsx}" (relative to the CSS file, not the project root).
PurgeCSS-style mental model
Symptom: Engineers add classes to a "safelist" that doesn't exist.
Diagnosis: v4 doesn't purge — it generates. There's no allowlist; if the class isn't in a scanned file, it doesn't exist.
Fix: Use a stub component or comment that contains the literal classes:
// Tailwind safelist (do not delete): bg-red-500 bg-green-500 bg-blue-500constSTATUS_COLORS = { error: 'bg-red-500', ok: 'bg-green-500', info: 'bg-blue-500' };
v3 plugins copy-pasted into v4
Symptom:Plugin "tailwindcss/typography" failed to load.Diagnosis: v4's plugin API is incompatible with most v3 plugins.
Fix: Use the v4-native plugin (@tailwindcss/typography v0.6+ for v4), or rewrite the plugin's effects with @variant / @layer components / custom utilities.
Quality gates
No template-literal class strings without an explicit safelist or CSS-variable fallback.
tailwind.config.js deleted (or absent from a fresh project).
Every workspace dep with Tailwind classes has an @source glob.
Dark-mode strategy documented in one place; not scattered across components.
CSS bundle <50KB gzipped for a typical app; <30KB for a marketing page.
Container queries used wherever a component is reused at different widths (cards, sidebars).
Theme tokens accessible from raw CSS (var(--color-brand-500)) and from utilities.
Deterministic Audit
Before committing to a v4 setup or migration (or reviewing another agent's), write it
as a JSON plan matching schemas/tailwind-v4-plan.schema.json and run the deterministic
auditor:
auditTailwindV4(plan) (in scripts/tailwind_v4_audit.mjs) turns this skill's
anti-patterns and Quality Gates into machine-checkable rules over structured fields —
no keyword matching: v3 @tailwind directives in a v4 build, a surviving
tailwind.config.js, template-literal class names Oxide cannot see, workspace deps
with Tailwind classes but no @source glob, a scattered dark-mode strategy, unported
v3 plugins, and a CSS bundle over budget. It returns
{ pass, score, findings, recommendations }. examples/sample-input.json is a
completed v4 migration plan (pass: true). Version history lives in CHANGELOG.md.
NOT for
Tailwind v3 — different config model, different engine.
CSS-in-JS (Emotion, styled-components) — pair with css-in-js-architect skill.
Vanilla CSS architecture (BEM, SMACSS, ITCSS) — different paradigm.
React Native — Tailwind variants for RN exist but have different constraints.
Before calling any rendered page, artifact, dashboard, deck, or component done,
run the mechanical overflow/collision checker. It renders the page headlessly and
flags text-vs-text collisions, clipped/ellipsis-truncated elements, text escaping
its container, and horizontal page scroll — the visual defects a screenshot hides
and that only appear at a specific width or in one theme.
Resolve layout-overflow-guard from the active skill catalog before running it.
The command below shows the standard Claude install path; use the path reported
by your harness. If the skill is absent, install or sync it instead of skipping
this gate.
You do not need to read check_layout.py — invoke it with the Bash tool and
act on its report and exit code (non-zero = a defect). The script's source never
enters your context; only its findings do. Drive it to zero violations across
every width and both themes before you ship. Full detail: the
layout-overflow-guard skill.