Tailwind CSS

Pathrule2 Rules • 3 Memories • 1 Skill

A pattern bundle for teams building UIs with Tailwind CSS v4. It encodes the CSS-first workflow (configure in CSS with @theme, no tailwind.config.js), enforces design tokens over arbitrary values, and standardizes class ordering and conditional-class handling. Use it so AI agents and humans write Tailwind that matches your design system instead of one-off magic numbers.

Suggested path map

Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.

/ workspace root
tailwind-css-review
src/
No dynamic Tailwind class string interpolation
Use design tokens, not arbitrary values
styles/
Tailwind v4 is CSS-first via @theme (no tailwind.config.js)
components/
Variant + merge stack: cva, clsx, tailwind-merge
@apply in scoped stylesheets needs @reference (and is a last resort)

Rules

2
No dynamic Tailwind class string interpolation/srchighstrictNever assemble Tailwind class names from runtime template strings; they get purged.
1Tailwind v4 scans source files as plain text and never parses them as code, so any class name assembled at runtime is invisible to the compiler and never generated. This is the single most common way Tailwind styling silently breaks in production.
2 
3- Do not write `className={`bg-${status}-500`}` or `text-{{ error ? 'red' : 'green' }}-600`. The complete literal token (`bg-red-500`, `text-red-600`) must appear verbatim somewhere in source.
4- Map state to full, static class strings via a lookup object or `class-variance-authority` (cva): `const variants = { error: 'text-red-600', ok: 'text-green-600' }` then `className={variants[state]}`.
5- Partial classes split across `clsx`/`cn` arguments also fail when a single variant (for example `md:flex`) is built from fragments. Keep each complete utility, including its modifier, in one string.
6- If a class genuinely must be produced from data the scanner cannot see (server-driven values), safelist it in CSS with `@source inline("bg-red-{50,{100..900..100},950}")` rather than interpolating it.
Use design tokens, not arbitrary values/srchighadvisoryPrefer theme-backed utilities over square-bracket arbitrary values.
1Reach for a standard token-backed utility before an arbitrary value so markup stays inside the design system and the output CSS stays lean.
2 
3- Replace one-offs like `w-[137px]`, `text-[#3b82f6]`, or `mt-[13px]` with token utilities (`w-32`, `text-primary`, `mt-3`).
4- If a value is genuinely reused, add it to `@theme` as a custom token (for example `--color-brand` or `--spacing-18`) instead of repeating the bracket form across files.
5- Arbitrary values are an escape hatch for true one-offs only; each distinct one emits its own hardcoded rule and bypasses the theme variable system, so a design token change will not reach them.
6- Note a known toolchain caveat (2026): some arbitrary values such as `aspect-[12/5]` have intermittently been dropped by the Turbopack-integrated compiler, which is one more reason to prefer real tokens.

Memories

3
Tailwind v4 is CSS-first via @theme (no tailwind.config.js)/src/stylesConfigure tokens in CSS with @theme; do not scaffold a JS config file.
1Tailwind CSS v4 moved configuration out of JavaScript into the stylesheet. Do not scaffold a `tailwind.config.js` for a fresh v4 project, and do not add a `content` array (v4 auto-detects template files).
2 
3- Start the entry stylesheet with `@import "tailwindcss";` then declare design tokens in an `@theme { ... }` block (`--color-*`, `--font-*`, `--breakpoint-*`, `--spacing-*`).
4- Theme variables become both real CSS custom properties and generated utilities, so `--color-brand: #...;` yields `bg-brand`, `text-brand`, `border-brand`, and friends automatically.
5- Use `@theme` only for tokens that should generate utilities. For plain CSS variables that should NOT mint utilities (for example values you only read in custom CSS), define them under `:root` instead.
6- Multi-theme / runtime theme switching is done by overriding the generated CSS variables under `:root`, `.dark`, or `[data-theme=...]` at runtime; no rebuild is needed.
7- Add custom utilities with `@utility` and custom states with `@custom-variant` rather than a JS plugin.
8 
9See /src/components for the cva + tailwind-merge composition stack and the @apply/@reference footgun in scoped stylesheets.
Variant + merge stack: cva, clsx, tailwind-merge/src/componentsStandardize one cn helper and define variants at module scope.
1Reusable components compose classes through one shared helper so conflicting utilities resolve predictably and caller overrides win.
2 
3- Create `cn` once and import it everywhere: `export const cn = (...i: ClassValue[]) => twMerge(clsx(i))`. `clsx` handles conditional/falsy class logic; `twMerge` resolves Tailwind conflicts so the last conflicting utility wins (so `cn('px-2', 'px-4')` yields `px-4`, not both).
4- Order inside `cn` matters: base styles first, conditional/variant classes next, the caller's `className` prop last, so consumers can always override.
5- Declare `cva` variant maps at module scope, never inside render, to avoid recreating them every render. Keep one complete class string per variant value (see the dynamic-class rule in /src).
6- Let `prettier-plugin-tailwindcss` own class ordering so diffs and reviews stay focused on logic, not sort churn. Do not reorder classes by hand.
@apply in scoped stylesheets needs @reference (and is a last resort)/src/componentsv4 @apply in SFC/module styles requires @reference; overusing it wrecks build times.
1In Tailwind v4, `@apply` and theme functions only know about your utilities and tokens inside the main stylesheet that imported Tailwind. Any separately-compiled stylesheet (a Vue/Svelte SFC `<style>` block, a CSS module, or any file processed in isolation) must opt in with `@reference` first, or `@apply` silently does nothing.
2 
3- At the top of such a block: `@reference "../app.css";` (point it at the stylesheet that runs `@import "tailwindcss"` plus your `@theme`). Without it, `@apply bg-brand` compiles to empty output.
4- Performance footgun: every `@reference` re-processes the full theme for that file. In monorepos with many component-level `@apply` usages this has blown startup times from seconds to minutes. Prefer plain utility classes in markup; if you only need bare CSS variables, reference them as `var(--color-brand)` directly rather than pulling in `@reference`.
5- Treat `@apply` as a last resort, mainly for styling markup you do not control (third-party widgets, `dangerouslySetInnerHTML` content). For your own components, compose utilities in JSX via the `cn` stack instead.

Skills

1
tailwind-css-review/rootPre-merge checklist for Tailwind v4 utility, token, and config hygiene.
1---
2name: tailwind-css-review
3description: Review Tailwind CSS v4 changes for token-driven styling, no arbitrary-value or dynamic-class sprawl, CSS-first config, correct @apply/@reference usage, and conflict-safe class merging before merging.
4---
5 
6# Tailwind CSS review
7 
8- [ ] No runtime-interpolated class strings (`bg-${x}-500`, `text-{{cond}}-600`); state mapped via cva or a literal lookup, or safelisted with `@source inline(...)`. Each complete utility (including modifiers like `md:`) lives in one string, not split across cn arguments.
9- [ ] No new arbitrary values (`w-[..]`, `text-[#..]`, `mt-[..px]`); reused values promoted to `@theme` tokens instead.
10- [ ] Configuration stays CSS-first: tokens in `@theme`, no `tailwind.config.js` reintroduced, no `content` array added.
11- [ ] `@theme` used only for tokens that should generate utilities; non-utility variables live under `:root`.
12- [ ] Any `@apply` in a scoped/SFC/module stylesheet has a matching `@reference` to the main stylesheet; `@apply` is not used where a plain utility class in markup would do.
13- [ ] Custom utilities use `@utility`; custom dark/state variants use `@custom-variant` (for example `@custom-variant dark (&:where(.dark, .dark *))`), not a JS plugin or `darkMode` config key.
14- [ ] Component classes merge through the shared `cn` helper so caller overrides win and conflicting utilities resolve.
15- [ ] cva variant maps defined at module scope, one full class string per variant value.
16- [ ] Classes sorted by `prettier-plugin-tailwindcss`; the diff has no manual reordering churn.
17- [ ] Responsive, dark, and state styling uses real modifiers (`md:`, `dark:`, `hover:`) rather than duplicated bespoke CSS.

Why this pattern

AI agents and rushed PRs fill Tailwind markup with arbitrary values and ad-hoc classes that drift from the design system and bloat the CSS bundle.

Built for Frontend and product teams building component-driven UIs with Tailwind CSS v4 and React or other JSX frameworks..

Keeps your assistant from:

  • Arbitrary values like w-[137px] or text-[#3b82f6] replacing real design tokens
  • Resurrecting a tailwind.config.js when v4 expects CSS-first @theme configuration
  • Dynamic class strings such as bg-${color}-500 that Tailwind cannot detect at build time
License
Apache-2.0
Version
1.0.0
Updated
2026-06-09
View source