Tailwind CSS v4 — Rules and Conventions
1. Philosophy
- CSS-first config —
@theme in CSS, no tailwind.config.js required.
- Utility-first — Compose designs from utilities. Extract components only when patterns repeat.
- Design tokens as CSS vars — Colors, spacing, fonts, shadows via
@theme → CSS variables.
- No runtime — All compiled at build. Zero client-side JS.
- Modern CSS — Uses cascade layers, container queries,
:has(), color-mix().
2. Minimum Version
| Technology |
Minimum Version |
| Tailwind CSS |
4.0+ |
| Node.js |
22+ |
| pnpm |
11+ |
3. Installation
Vite (recommended)
pnpm add -D tailwindcss@4 @tailwindcss/vite
// vite.config.ts
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [tailwindcss()],
});
/* styles/main.css */
@import "tailwindcss";
@theme {
/* Design tokens here */
}
PostCSS (standalone)
pnpm add -D tailwindcss@4 @tailwindcss/postcss postcss
// postcss.config.js
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
CLI (no build tool)
pnpm add -D tailwindcss@4
npx tailwindcss -i ./styles/main.css -o ./dist/styles.css --watch
4. @import and Directives
/* styles/main.css */
@import "tailwindcss";
/* Or explicit layers */
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
/* Custom layers */
@layer components {
.btn {
@apply px-4 py-2 rounded;
}
}
Rules
- Single
@import "tailwindcss" — imports all layers
@layer for custom utilities/components — respects cascade order
- No
@tailwind directives — v4 uses @import
5. @theme — Design Tokens
@theme {
/* Colors (generates --color-*) */
--color-primary: #0066cc;
--color-primary-50: #e6f0fa;
--color-primary-100: #cce0f5;
--color-primary-500: #0066cc;
--color-primary-900: #003366;
/* Spacing (generates --spacing-*) */
--spacing-18: 4.5rem;
--spacing-88: 22rem;
/* Fonts (generates --font-*) */
--font-sans: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", monospace;
/* Border radius (generates --radius-*) */
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
/* Shadows (generates --shadow-*) */
--shadow-glow: 0 0 20px rgb(0 102 204 / 0.3);
/* Breakpoints (generates --breakpoint-*) */
--breakpoint-3xl: 120rem;
/* Custom tokens */
--animate-duration-fast: 150ms;
--animate-duration-normal: 250ms;
}
Generated utilities
| Token prefix |
Utility prefix |
Example |
--color-* |
bg-, text-, border-, ring- |
bg-primary-500 |
--spacing-* |
p-, m-, gap-, space- |
p-18 |
--font-* |
font- |
font-sans |
--radius-* |
rounded- |
rounded-xl |
--shadow-* |
shadow- |
shadow-glow |
--breakpoint-* |
sm:, md:, etc. |
3xl:flex |
--animate-* |
animate- |
animate-duration-fast |
Rules Theme
- All tokens in
@theme — single source of truth
- Semantic names —
primary, secondary, not blue-500
- CSS variable fallback —
var(--color-primary, #0066cc)
6. Essential Utilities (Table)
Layout
| Utility |
CSS |
Responsive |
container |
max-width: 1280px; margin: auto |
✓ |
flex / grid / block / hidden |
display |
✓ |
flex-row / flex-col |
flex-direction |
✓ |
items-center / justify-center / gap-4 |
align/justify/gap |
✓ |
w-full / h-screen / min-h-0 |
width/height |
✓ |
Spacing
| Pattern |
Values |
p-{0..4..96} / m-{0..4..96} |
padding/margin |
px-4 / py-2 / pt-8 |
directional |
gap-4 / space-y-4 |
flex/grid gap |
Typography
| Utility |
Values |
text-{xs..9xl} |
font-size |
font-{thin..extrabold} |
font-weight |
font-sans / font-mono |
font-family |
leading-{tight..loose} |
line-height |
tracking-{tight..wide} |
letter-spacing |
Colors
| Utility |
Token |
bg-primary-500 |
--color-primary-500 |
text-gray-900 |
--color-gray-900 |
border-primary-200 |
--color-primary-200 |
ring-primary-500 |
--color-primary-500 |
Borders & Radius
| Utility |
Values |
border / border-2 |
width |
rounded / rounded-lg / rounded-xl / rounded-full |
radius |
border-gray-200 / border-primary-500 |
color |
Shadows
| Utility |
Token |
shadow / shadow-lg / shadow-xl |
default scale |
shadow-glow |
custom from @theme |
7. Responsive Design
Breakpoints (mobile-first)
@theme {
--breakpoint-sm: 40rem; /* 640px */
--breakpoint-md: 48rem; /* 768px */
--breakpoint-lg: 64rem; /* 1024px */
--breakpoint-xl: 80rem; /* 1280px */
--breakpoint-2xl: 96rem; /* 1536px */
}
Usage
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="p-4 md:p-6 lg:p-8">Content</div>
</div>
Container queries
<div class="@container">
<div class="@md:grid @md:grid-cols-2">...</div>
</div>
8. Variants (States)
| Variant |
Trigger |
Example |
hover: |
Mouse hover |
hover:bg-primary-600 |
focus: / focus-visible: |
Keyboard focus |
focus:ring-2 |
active: |
Mouse down |
active:scale-95 |
disabled: |
Disabled state |
disabled:opacity-50 |
group-hover: |
Parent hover |
group:hover .child:opacity-100 |
peer-checked: |
Sibling checked |
peer:checked ~ .indicator |
data-[state=open]: |
Data attribute |
data-[state=open]:animate-in |
dark: |
Dark mode |
dark:bg-gray-900 |
supports-[display:grid]: |
@supports |
supports-[display:grid]:grid |
9. Dark Mode
Class strategy (recommended)
@theme {
--color-bg: #ffffff;
--color-text: #111827;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #111827;
--color-text: #f9fafb;
}
}
/* Or explicit class */
.dark {
--color-bg: #111827;
--color-text: #f9fafb;
}
<html class="dark">
<!-- Force dark -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100"></div>
</html>
Rules Dark Mode
- CSS variables in
@theme — automatic dark mode via media query
dark: variant — for explicit overrides
color-scheme meta — <meta name="color-scheme" content="light dark">
10. Arbitrary Values
<!-- Arbitrary spacing -->
<div class="p-[18px] mt-[var(--custom-spacing)]">
<!-- Arbitrary color -->
<div class="bg-[#0066cc] text-[#f0f0f0]">
<!-- Arbitrary grid -->
<div class="grid-cols-[1fr_300px_auto]">
<!-- Arbitrary animation -->
<div class="animate-[spin_1s_linear_infinite]"></div>
</div>
</div>
</div>
Rules Arbitrary Value
- Square brackets —
[value] for any valid CSS value
- CSS variables —
var(--token) inside brackets
- Last resort — prefer design tokens in
@theme
11. @apply / @utility
@apply (component extraction)
@layer components {
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-colors;
}
.btn-primary {
@apply btn bg-primary-500 text-white hover:bg-primary-600;
}
.btn-secondary {
@apply btn bg-gray-200 text-gray-900 hover:bg-gray-300;
}
}
@utility (custom utilities)
@layer utilities {
@utility text-balance {
text-wrap: balance;
}
@utility scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
}
Rules Apply Utilities
@apply in @layer components — for repeated UI patterns
@utility in @layer utilities — for single-purpose utilities
- Avoid over-extraction — utilities are fine for one-offs
12. Essential Plugins
pnpm add -D @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio
/* styles/main.css */
@import "tailwindcss";
@plugin "@tailwindcss/forms";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/aspect-ratio";
@theme { ... }
| Plugin |
Purpose |
@tailwindcss/forms |
Reset + style form inputs |
@tailwindcss/typography |
.prose for rich text |
@tailwindcss/aspect-ratio |
aspect-video, aspect-square |
13. Framework Integration
Vite
pnpm add -D tailwindcss@4 @tailwindcss/vite
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({ plugins: [tailwindcss()] });
Astro
pnpm astro add tailwind
# or
pnpm add -D tailwindcss@4 @tailwindcss/vite
// astro.config.mjs
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
integrations: [],
vite: { plugins: [tailwindcss()] },
});
Vite/Astro details: see vite and astro skills.
14. Class Order (Prettier Plugin)
pnpm add -D prettier prettier-plugin-tailwindcss
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./styles/main.css"
}
Automatic sorting
<!-- Before (unsorted) -->
<div class="text-center p-4 bg-white rounded-lg shadow-lg md:flex">
<!-- After (sorted by Prettier) -->
<div class="bg-white p-4 rounded-lg shadow-lg text-center md:flex"></div>
</div>
Order groups
- Layout (
flex, grid, container)
- Spacing (
p-4, m-2, gap-4)
- Sizing (
w-full, h-10)
- Typography (
text-center, font-bold)
- Colors (
bg-white, text-gray-900)
- Borders (
rounded-lg, border)
- Effects (
shadow-lg, opacity-50)
- Transforms (
rotate-45, scale-100)
- Transitions (
transition, duration-200)
- Interactivity (
hover:, focus:, dark:)
15. Methodology
Before using ANY Tailwind class/pattern not documented in this skill:
- MCP Context7 (priority):
context7_resolve-library-id + context7_query-docs for Tailwind CSS.
- Official docs: tailwindcss.com — verify current utilities + config.
- Project config:
styles/main.css, vite.config.ts, package.json — verify against actual setup.
- HARD RULE: If not in this skill AND cannot be verified against 2 authoritative sources → DO NOT USE IT. Document as assumption or risk in report to orchestrator.
16. Prohibitions
- ❌ Do not use
tailwind.config.js — v4 uses @theme in CSS
- ❌ Do not use
@tailwind base/components/utilities
— use @import "tailwindcss"
- ❌ Do not extract every pattern with
@apply — utilities are fine for one-offs
- ❌ Do not use arbitrary values when token exists — use
@theme
- ❌ Do not disable Prettier plugin — class order matters for diffs
- ❌ Do not use
!important in utilities — use cascade layers
- ❌ Do not skip
@plugin for forms/typography — better defaults
17. References
Note: For CSS conventions, see CSS
Note: For package manager conventions, see Package Manager
Note: For Vite integration, see Vite
Note: For Astro integration, see Astro
Note: For performance (Core Web Vitals), see Performance
Note: For accessibility (WCAG), see Accessibility
Last updated: 2026-08