Tailwind CSS v4 — Claude Code Skill
Tailwind CSS v4 (released January 2025, current stable v4.2) uses a CSS-first
configuration approach. This skill covers v4 exclusively. All theme customization
happens in CSS via @theme, not in tailwind.config.js.
Critical: v3 → v4 Breaking Changes
Do NOT use these v3 patterns — they are wrong for v4:
| ❌ v3 (WRONG) |
✅ v4 (CORRECT) |
@tailwind base; @tailwind components; @tailwind utilities; |
@import "tailwindcss"; |
tailwind.config.js theme/extend |
@theme { --color-brand: ...; } in CSS |
content: ['./src/**/*.{js,tsx}'] in JS config |
Automatic detection + @source directive |
theme() function in CSS |
var(--color-red-500) CSS variables |
!bg-red-500 (prefix ! for important) |
bg-red-500! (suffix !) |
darkMode: 'class' in JS config |
@custom-variant dark (&:where(.dark, .dark *)); |
addUtility() in plugin JS |
@utility name { ... } in CSS |
addVariant() in plugin JS |
@custom-variant name (selector); in CSS |
@screen md { ... } |
@variant md { ... } or just use md: prefix |
@apply in Vue/Svelte <style> (breaks) |
Add @reference "../../app.css"; first |
require('@tailwindcss/container-queries') |
Built-in: @container + @sm: / @md: etc. |
bg-[var(--my-color)] |
bg-(--my-color) (v4 shorthand) |
Setup
CSS Entry Point
/* app.css — the single import replaces all v3 @tailwind directives */
@import "tailwindcss";
/* Optional modifiers: */
@import "tailwindcss" prefix(tw); /* Prefix all classes: tw:flex */
@import "tailwindcss" important; /* All utilities get !important */
@import "tailwindcss" source("../src"); /* Set source scanning root */
Vite Setup
npm install -D tailwindcss @tailwindcss/vite
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [tailwindcss()],
})
PostCSS Setup (Alternative)
npm install -D tailwindcss @tailwindcss/postcss autoprefixer
// postcss.config.js
export default {
plugins: {
"@tailwindcss/postcss": {},
autoprefixer: {},
},
}
CLI Setup (No Bundler)
npm install -D tailwindcss @tailwindcss/cli
npx @tailwindcss/cli -i app.css -o dist/app.css --watch
Reference Material
A comprehensive Tailwind CSS v4.2 reference document is available at:
../../guides/tailwindcss-v4/reference.md
(Relative to this SKILL.md file.)
When to consult the reference:
- You need the exact list of values for a specific utility (e.g., all
blur-* sizes, all cursor-* options)
- You're unsure whether a utility exists in v4 or was removed/renamed from v3
- You need the precise
@theme namespace for a category (e.g., which variable prefix generates which utilities)
- You want to verify the correct syntax for a v4 directive (
@utility, @custom-variant, @source, @variant)
- You encounter an unfamiliar v4 feature (container queries,
field-sizing, text-shadow, mask utilities, @reference)
When you don't need it:
- Standard layout, spacing, typography, color, and responsive patterns are covered in this SKILL.md
- Common component patterns (cards, navs, forms, grids) are already here
- The v3→v4 migration table at the top of this file covers the most common pitfalls
Read the reference with the view tool if you need detail beyond what this skill file provides.
Theme System (@theme)
Theme variables are special CSS variables defined with @theme that create
corresponding utility classes. They are not just CSS variables — they instruct
Tailwind to generate utilities.
Defining Theme Variables
@import "tailwindcss";
@theme {
/* Colors → bg-brand, text-brand, border-brand, ring-brand, etc. */
--color-brand-50: oklch(0.97 0.02 264);
--color-brand-500: oklch(0.55 0.22 264);
--color-brand-900: oklch(0.25 0.15 264);
/* Fonts → font-display, font-body utilities */
--font-display: "Satoshi", "Inter", sans-serif;
--font-body: "Inter", system-ui, sans-serif;
/* Font sizes → text-display utility */
--text-display: 3rem;
/* Font weights → font-heading utility */
--font-weight-heading: 700;
/* Letter spacing → tracking-wide utility */
--tracking-wide: 0.025em;
/* Line height → leading-relaxed utility */
--leading-relaxed: 1.75;
/* Breakpoints → 3xl: responsive variant */
--breakpoint-3xl: 120rem;
/* Container sizes → @8xl: container query variant + max-w-8xl */
--container-8xl: 96rem;
/* Border radius → rounded-xl utility */
--radius-xl: 0.75rem;
/* Shadows → shadow-soft utility */
--shadow-soft: 0 2px 8px rgba(0, 0, 0, 0.08);
/* Inset shadows → inset-shadow-deep utility */
--inset-shadow-deep: inset 0 4px 8px rgba(0, 0, 0, 0.15);
/* Drop shadows → drop-shadow-hard utility */
--drop-shadow-hard: 0 2px 4px rgba(0, 0, 0, 0.3);
/* Blur → blur-heavy utility */
--blur-heavy: 40px;
/* Perspective → perspective-dramatic utility */
--perspective-dramatic: 200px;
/* Aspect ratio → aspect-golden utility */
--aspect-golden: 1.618 / 1;
/* Easing → ease-snappy utility */
--ease-snappy: cubic-bezier(0.2, 0, 0, 1);
/* Animations → animate-fade-in utility */
--animate-fade-in: fade-in 0.3s ease-out;
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
}
Namespace → Utility Mapping
| Theme Namespace |
Creates |
--color-* |
bg-*, text-*, border-*, ring-*, fill-*, stroke-*, accent-*, caret-*, outline-*, decoration-*, shadow-* (color), from-*, via-*, to-* |
--font-* |
font-* (family) |
--text-* |
text-* (size) |
--font-weight-* |
font-* (weight) |
--tracking-* |
tracking-* (letter-spacing) |
--leading-* |
leading-* (line-height) |
--breakpoint-* |
sm:, md:, lg:, etc. responsive variants |
--container-* |
@sm:, @md:, etc. container query variants + max-w-* |
--spacing-* |
p-*, m-*, gap-*, w-*, h-*, and all spacing utilities |
--radius-* |
rounded-* |
--shadow-* |
shadow-* |
--inset-shadow-* |
inset-shadow-* |
--drop-shadow-* |
drop-shadow-* |
--blur-* |
blur-* |
--perspective-* |
perspective-* |
--aspect-* |
aspect-* |
--ease-* |
ease-* |
--animate-* |
animate-* |
Overriding & Removing Defaults
@theme {
/* Override one value */
--breakpoint-sm: 30rem;
/* Remove entire namespace, then redefine */
--color-*: initial;
--color-white: #fff;
--color-black: #000;
--color-primary: oklch(0.6 0.2 260);
/* Remove a single default */
--breakpoint-2xl: initial;
/* Reset everything (for fully custom themes) */
--*: initial;
}
Referencing Other Variables (use inline)
@theme inline {
--font-sans: var(--font-inter);
}
The inline option inlines the value rather than referencing the theme variable,
avoiding CSS variable resolution scope issues.
CSS Directives
@source — Register Additional Scan Paths
@source "../node_modules/@my-company/ui-lib";
@source "../../packages/shared-components";
@source not() — Ignore Paths
@source not("./src/legacy");
@source inline() — Safelist Classes
@source inline("underline bg-red-500 lg:flex");
@utility — Custom Utilities
/* Simple utility */
@utility content-auto {
content-visibility: auto;
}
/* With nesting */
@utility scrollbar-hidden {
&::-webkit-scrollbar {
display: none;
}
}
/* Functional utility (accepts values) */
@utility tab-* {
tab-size: --value(--tab-size-*); /* Match theme keys */
}
@utility tab-* {
tab-size: --value(integer); /* Bare integer values: tab-4 */
}
/* Multiple resolvers (first match wins) */
@utility tab-* {
tab-size: --value(--tab-size-*, integer, "inherit", "initial");
}
@custom-variant — Custom Variants
/* Class-based dark mode (replaces v3 darkMode: 'class') */
@custom-variant dark (&:where(.dark, .dark *));
/* Data attribute dark mode */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
/* Custom theme variant */
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
@variant — Apply Variants in Custom CSS
.my-element {
background: white;
@variant dark {
background: black;
}
@variant hover {
@variant dark {
background: #333;
}
}
}
@layer — Custom CSS in Layers
@layer base {
h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-xl); }
}
@layer components {
.card {
background-color: var(--color-white);
border-radius: var(--radius-lg);
padding: calc(var(--spacing) * 6);
box-shadow: var(--shadow-xl);
}
.btn-primary {
@apply rounded-lg bg-blue-600 px-4 py-2 font-semibold text-white;
@apply hover:bg-blue-700 active:bg-blue-800;
@apply focus:outline-2 focus:outline-offset-2 focus:outline-blue-500;
}
}
@reference — For Vue/Svelte <style> Blocks
<style>
@reference "../../app.css";
h1 {
@apply text-2xl font-bold text-red-500;
}
</style>
@apply — Inline Utility Classes in CSS
.select2-dropdown {
@apply rounded-b-lg shadow-md;
}
Legacy Compatibility
@config "../../tailwind.config.js"; /* Load v3 JS config */
@plugin "@tailwindcss/typography"; /* Load JS plugin */
CSS Functions
/* Adjust color opacity at build time */
.element { color: --alpha(var(--color-lime-300) / 50%); }
/* Generate spacing from theme multiplier */
.element { margin: --spacing(4); } /* = calc(var(--spacing) * 4) */
/* In arbitrary values: */
<div class="py-[calc(--spacing(4)-1px)]">
Responsive Design
Default Breakpoints (Mobile-First)
| Prefix |
Min Width |
CSS |
| (none) |
0 |
Base styles — apply to ALL sizes |
sm: |
40rem (640px) |
@media (width >= 40rem) |
md: |
48rem (768px) |
@media (width >= 48rem) |
lg: |
64rem (1024px) |
@media (width >= 64rem) |
xl: |
80rem (1280px) |
@media (width >= 80rem) |
2xl: |
96rem (1536px) |
@media (width >= 96rem) |
Max-Width & Range Queries
<div class="max-md:flex"> <!-- Below 768px only -->
<div class="md:max-xl:flex"> <!-- Between 768px and 1280px -->
<div class="min-[900px]:flex"> <!-- Arbitrary min-width -->
<div class="max-[600px]:hidden"> <!-- Arbitrary max-width -->
Mobile-First Pattern
<!-- CORRECT: style mobile first, override upward -->
<div class="text-center sm:text-left">
<!-- WRONG: don't use sm: to "target mobile" -->
<div class="sm:text-center"> <!-- This only applies at 640px+ -->
Responsive Example
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div class="rounded-lg bg-white p-4 shadow">Item</div>
<!-- ... -->
</div>
<h1 class="text-2xl font-bold md:text-4xl lg:text-6xl">
Responsive heading
</h1>
<div class="hidden lg:block">Desktop only</div>
<div class="lg:hidden">Mobile/tablet only</div>
Container Queries (Built-In)
<!-- Mark parent as container -->
<div class="@container">
<!-- Style based on container width, not viewport -->
<div class="flex flex-col @md:flex-row @md:items-center gap-4">
<img class="w-full @md:w-48 rounded-lg" src="..." alt="">
<div>
<h3 class="text-lg font-bold">Title</h3>
<p class="text-sm text-gray-500">Description</p>
</div>
</div>
</div>
<!-- Named containers for nested queries -->
<div class="@container/main">
<div class="@sm/main:flex-row flex flex-col">...</div>
</div>
<!-- Max-width container queries -->
<div class="@container">
<div class="flex flex-row @max-md:flex-col">...</div>
</div>
<!-- Range queries -->
<div class="@container">
<div class="@sm:@max-md:flex-col">Between sm and md container</div>
</div>
Dark Mode
Automatic (System Preference — Default)
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Switches with OS setting
</div>
Manual Toggle (Class-Based)
/* In your CSS */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
<html class="dark">
<body>
<div class="bg-white dark:bg-gray-900">Now class-controlled</div>
</body>
</html>
// Toggle in JS
document.documentElement.classList.toggle('dark',
localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
);
Data Attribute Approach
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
<html data-theme="dark">...</html>
State Variants
Interactive States
<button class="bg-blue-600 hover:bg-blue-700 active:bg-blue-800
focus:outline-2 focus:outline-offset-2 focus:outline-blue-500
disabled:opacity-50 disabled:cursor-not-allowed
transition-colors">
Button
</button>
<a class="text-blue-600 hover:text-blue-800 hover:underline
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500
visited:text-purple-600">
Link
</a>
Form States
<input class="border border-gray-300 rounded-lg px-3 py-2
focus:border-blue-500 focus:ring-2 focus:ring-blue-200
invalid:border-red-500 focus:invalid:ring-red-200
disabled:bg-gray-100 disabled:cursor-not-allowed
placeholder:text-gray-400 placeholder:italic
read-only:bg-gray-50"
required />
<input type="checkbox" class="checked:bg-blue-500 indeterminate:bg-gray-300" />
Structural (Children)
<ul>
{items.map(item => (
<li class="py-4 first:pt-0 last:pb-0 odd:bg-gray-50 even:bg-white">
{item.name}
</li>
))}
</ul>
<div class="nth-3:underline">3rd child underlined</div>
<div class="empty:hidden">Hidden when empty</div>
Group (Parent State)
<a href="#" class="group block rounded-lg p-6 hover:bg-gray-50">
<h3 class="font-semibold group-hover:text-blue-600">Title</h3>
<p class="text-gray-500 group-hover:text-gray-700">Description</p>
<svg class="hidden group-has-[a]:block">...</svg>
</a>
<!-- Named groups for nesting -->
<div class="group/card">
<div class="group/button">
<span class="group-hover/card:text-blue-500
group-hover/button:underline">Text</span>
</div>
</div>
Peer (Sibling State)
<label class="block">
<input type="email" class="peer rounded border px-3 py-2" required />
<p class="invisible mt-1 text-sm text-red-500 peer-invalid:visible">
Please enter a valid email.
</p>
</label>
<!-- Named peers -->
<input id="draft" class="peer/draft" type="radio" name="status" checked />
<div class="hidden peer-checked/draft:block">Draft selected</div>
Implicit Group (in-*)
<!-- No need to add "group" class to parent -->
<div tabindex="0">
<div class="opacity-50 in-focus:opacity-100">
Responds to any parent's focus
</div>
</div>
Has (Descendant State)
<label class="has-checked:bg-indigo-50 has-checked:ring-indigo-200">
<input type="radio" class="checked:border-indigo-500" />
Google Pay
</label>
Not
<button class="bg-indigo-600 hover:not-focus:bg-indigo-700">
Only hover styles when NOT focused
</button>
Pseudo-Elements
<div class="before:content-[''] before:absolute before:inset-0 before:bg-black/50">
Overlay
</div>
<blockquote class="after:content-[attr(data-cite)]">Quote</blockquote>
<input class="placeholder:text-gray-400" placeholder="Search..." />
<li class="marker:text-blue-500">List item</li>
<p class="selection:bg-yellow-200">Selectable text</p>
<input type="file" class="file:mr-4 file:rounded-lg file:border-0 file:bg-blue-50 file:px-4 file:py-2" />
Media & Feature Queries
<div class="motion-safe:animate-bounce">Respects reduced motion</div>
<div class="motion-reduce:transition-none">No transitions if reduced motion</div>
<div class="contrast-more:border-2">Higher contrast borders</div>
<div class="print:hidden">Hidden when printing</div>
<div class="portrait:flex-col landscape:flex-row">Orientation-aware</div>
<div class="supports-[display:grid]:grid">Feature detection</div>
<div class="forced-colors:border">Forced colors mode</div>
Data & ARIA Attributes
<div class="data-[state=open]:bg-blue-50" data-state="open">Open</div>
<div class="aria-expanded:rotate-180" aria-expanded="true">Chevron</div>
<div class="aria-busy:opacity-50" aria-busy="true">Loading</div>
<details class="open:bg-gray-50" open>Expandable</details>
Stacking Variants
<button class="dark:md:hover:bg-indigo-600">
Dark mode + medium screen + hover
</button>
<div class="dark:lg:data-[active]:hover:bg-fuchsia-600">
Four stacked variants
</div>
Arbitrary Variants
<div class="[&>p]:mt-4">Direct child p tags</div>
<div class="[&_p]:mt-4">Any descendant p tags</div>
<div class="lg:[&:nth-child(-n+3)]:hover:underline">Complex selector</div>
<div class="[@supports(display:grid)]:grid">@supports</div>
Spacing System
In v4, spacing uses a single --spacing multiplier (default 0.25rem):
p-0 → padding: 0
p-px → padding: 1px
p-0.5 → padding: calc(var(--spacing) * 0.5) = 0.125rem
p-1 → padding: calc(var(--spacing) * 1) = 0.25rem
p-2 → padding: calc(var(--spacing) * 2) = 0.5rem
p-4 → padding: calc(var(--spacing) * 4) = 1rem
p-8 → padding: calc(var(--spacing) * 8) = 2rem
p-16 → 4rem, p-24 → 6rem, p-32 → 8rem, p-64 → 16rem, p-96 → 24rem
Any number works: p-3.5, p-7, p-18, p-72. The same scale applies to
margin, gap, width, height, inset, translate, scroll-margin, scroll-padding,
space-between, text-indent, border-spacing, and all spacing-based utilities.
Arbitrary Values
<div class="top-[117px]"> <!-- Any CSS value -->
<div class="bg-[#bada55]"> <!-- Hex color -->
<div class="text-[22px]"> <!-- Font size -->
<div class="grid-cols-[1fr_500px_2fr]"> <!-- Underscores = spaces -->
<div class="bg-[url('/img.png')]"> <!-- URLs -->
<div class="before:content-['Hello']"> <!-- Content strings -->
<div class="max-h-[calc(100dvh-4rem)]"> <!-- calc() -->
<div class="[mask-type:luminance]"> <!-- Arbitrary CSS property -->
<div class="[--my-var:1rem]"> <!-- CSS variable -->
<!-- CSS variable shorthand (v4): adds var() automatically -->
<div class="fill-(--my-brand-color)"> <!-- = fill-[var(--my-brand-color)] -->
<!-- Type hints for ambiguous variables -->
<div class="text-(length:--my-var)"> <!-- Force font-size interpretation -->
<div class="text-(color:--my-var)"> <!-- Force color interpretation -->
Important Modifier
<!-- v4: suffix ! -->
<div class="bg-red-500!">
<!-- v3 (WRONG in v4): prefix ! -->
<!-- <div class="!bg-red-500"> -->
Opacity Modifier
<div class="bg-red-500/50"> <!-- 50% opacity -->
<div class="bg-red-500/[.15]"> <!-- Arbitrary opacity -->
<div class="text-black/75"> <!-- 75% opacity text -->
Negative Values
<div class="-mt-4"> <!-- Negative margin -->
<div class="-translate-x-1/2"> <!-- Negative translate -->
<div class="-rotate-45"> <!-- Negative rotation -->
Color System
Default Palette
22 color families, each with shades 50–950:
slate, gray, zinc, neutral, stone, red, orange, amber,
yellow, lime, green, emerald, teal, cyan, sky, blue,
indigo, violet, purple, fuchsia, pink, rose.
Plus: black, white, transparent, current, inherit.
All default colors use oklch color space.
Custom Colors
@theme {
--color-brand-50: oklch(0.97 0.02 264);
--color-brand-500: oklch(0.55 0.22 264);
--color-brand-900: oklch(0.25 0.15 264);
}
Now bg-brand-500, text-brand-50, border-brand-900, etc. all work.
Complete Utility Reference
Layout
| Classes |
CSS Property |
block, inline-block, inline, flex, inline-flex, grid, inline-grid, table, contents, flow-root, list-item, hidden |
display |
aspect-auto, aspect-square, aspect-video, aspect-[4/3] |
aspect-ratio |
columns-1–columns-12, columns-auto, columns-3xs–columns-7xl |
columns |
box-border, box-content |
box-sizing |
float-start, float-end, float-right, float-left, float-none |
float |
clear-start, clear-end, clear-both, clear-none |
clear |
isolate, isolation-auto |
isolation |
object-contain, object-cover, object-fill, object-none, object-scale-down |
object-fit |
object-bottom, object-center, object-left, object-top, etc. |
object-position |
overflow-auto, overflow-hidden, overflow-clip, overflow-visible, overflow-scroll, overflow-x-*, overflow-y-* |
overflow |
overscroll-auto, overscroll-contain, overscroll-none |
overscroll-behavior |
static, fixed, absolute, relative, sticky |
position |
inset-*, inset-x-*, inset-y-*, top-*, right-*, bottom-*, left-*, start-*, end-* |
inset / TRBL |
visible, invisible, collapse |
visibility |
z-0, z-10, z-20, z-30, z-40, z-50, z-auto |
z-index |
Flexbox & Grid
| Classes |
CSS Property |
flex-row, flex-row-reverse, flex-col, flex-col-reverse |
flex-direction |
flex-wrap, flex-wrap-reverse, flex-nowrap |
flex-wrap |
flex-1, flex-auto, flex-initial, flex-none |
flex shorthand |
grow, grow-0 |
flex-grow |
shrink, shrink-0 |
flex-shrink |
basis-* (numbers, auto, full, fractions) |
flex-basis |
order-* (1–12, first, last, none) |
order |
grid-cols-* (1–12, none, subgrid) |
grid-template-columns |
grid-rows-* (1–12, none, subgrid) |
grid-template-rows |
col-span-*, col-start-*, col-end-* |
grid-column |
row-span-*, row-start-*, row-end-* |
grid-row |
grid-flow-row, grid-flow-col, grid-flow-dense |
grid-auto-flow |
auto-cols-auto, auto-cols-min, auto-cols-max, auto-cols-fr |
grid-auto-columns |
auto-rows-auto, auto-rows-min, auto-rows-max, auto-rows-fr |
grid-auto-rows |
gap-*, gap-x-*, gap-y-* |
gap |
justify-start, justify-end, justify-center, justify-between, justify-around, justify-evenly |
justify-content |
justify-items-start, justify-items-end, justify-items-center, justify-items-stretch |
justify-items |
justify-self-auto, justify-self-start, justify-self-end, justify-self-center |
justify-self |
items-start, items-end, items-center, items-baseline, items-stretch |
align-items |
content-center, content-start, content-end, content-between, content-around |
align-content |
self-auto, self-start, self-end, self-center, self-stretch |
align-self |
place-content-*, place-items-*, place-self-* |
Place shorthands |
Sizing
| Classes |
CSS Property |
w-* (numbers, auto, full, screen, dvw, svw, lvw, min, max, fit, fractions) |
width |
h-* (numbers, auto, full, screen, dvh, svh, lvh, min, max, fit) |
height |
size-* (sets both width and height) |
width + height |
min-w-*, max-w-* (none, full, min, max, fit, prose, screen-*, container sizes) |
min/max-width |
min-h-*, max-h-* |
min/max-height |
Typography
| Classes |
CSS Property |
font-sans, font-serif, font-mono |
font-family |
text-xs–text-9xl, text-sm/6 (combined size/line-height) |
font-size |
font-thin(100)–font-black(900) |
font-weight |
italic, not-italic |
font-style |
antialiased, subpixel-antialiased |
font-smoothing |
tracking-tighter–tracking-widest |
letter-spacing |
leading-none(1)–leading-loose(2) |
line-height |
text-left, text-center, text-right, text-justify, text-start, text-end |
text-align |
text-* (colors: text-gray-900, text-blue-500/50) |
color |
underline, overline, line-through, no-underline |
text-decoration-line |
decoration-* (color, style, thickness) |
text-decoration-* |
uppercase, lowercase, capitalize, normal-case |
text-transform |
truncate, text-ellipsis, text-clip |
text-overflow |
text-wrap, text-nowrap, text-balance, text-pretty |
text-wrap |
line-clamp-1–line-clamp-6, line-clamp-none |
Line clamping |
whitespace-normal, whitespace-nowrap, whitespace-pre, whitespace-pre-line, whitespace-pre-wrap |
white-space |
break-normal, break-words, break-all, break-keep |
word-break |
list-none, list-disc, list-decimal, list-inside, list-outside |
list-style |
tabular-nums, oldstyle-nums, lining-nums, proportional-nums, slashed-zero, ordinal, diagonal-fractions, stacked-fractions |
font-variant-numeric |
indent-* |
text-indent |
align-baseline, align-top, align-middle, align-bottom |
vertical-align |
content-none, content-[value] |
content |
Backgrounds
| Classes |
CSS Property |
bg-* (colors, opacity: bg-red-500/50) |
background-color |
bg-none, bg-linear-to-t/tr/r/br/b/bl/l/tl, bg-radial-*, bg-conic-* |
background-image |
from-*, via-*, to-* (gradient stops with colors + positions) |
Gradient color stops |
bg-fixed, bg-local, bg-scroll |
background-attachment |
bg-clip-border, bg-clip-padding, bg-clip-content, bg-clip-text |
background-clip |
bg-auto, bg-cover, bg-contain |
background-size |
bg-center, bg-top, bg-bottom, bg-left, bg-right, etc. |
background-position |
bg-repeat, bg-no-repeat, bg-repeat-x, bg-repeat-y |
background-repeat |
Borders
| Classes |
CSS Property |
rounded-none–rounded-full, per-side: rounded-t-*, rounded-tl-*, rounded-s-*, rounded-ss-* |
border-radius |
border, border-0, border-2, border-4, border-8, per-side: border-t-*, border-x-*, border-y-* |
border-width |
border-* (colors), per-side: border-t-red-500 |
border-color |
border-solid, border-dashed, border-dotted, border-double, border-hidden, border-none |
border-style |
outline, outline-0–outline-8 |
outline-width |
outline-* (colors) |
outline-color |
outline-none, outline-dashed, outline-dotted, outline-double |
outline-style |
outline-offset-* (0, 1, 2, 4, 8) |
outline-offset |
ring, ring-0–ring-8, ring-inset, ring-* (color) |
Ring (box-shadow) |
divide-x-*, divide-y-*, divide-* (color/style), divide-x-reverse |
Border between children |
Effects
| Classes |
CSS Property |
shadow-2xs–shadow-2xl, shadow-inner, shadow-none, shadow-* (color) |
box-shadow |
text-shadow-2xs–text-shadow-lg, text-shadow-none |
text-shadow (new in v4) |
opacity-0–opacity-100 |
opacity |
mix-blend-* (normal, multiply, screen, overlay, etc.) |
mix-blend-mode |
bg-blend-* |
background-blend-mode |
mask-* (clip, composite, image, mode, origin, position, repeat, size, type) |
CSS masks (new in v4) |
inset-shadow-* |
Inset box shadows (new in v4) |
Filters
| Classes |
CSS Property |
blur-none, blur-xs–blur-3xl |
filter: blur() |
brightness-0–brightness-200 |
filter: brightness() |
contrast-0–contrast-200 |
filter: contrast() |
grayscale, grayscale-0 |
filter: grayscale() |
hue-rotate-0–hue-rotate-180 |
filter: hue-rotate() |
invert, invert-0 |
filter: invert() |
saturate-0–saturate-200 |
filter: saturate() |
sepia, sepia-0 |
filter: sepia() |
drop-shadow-none–drop-shadow-2xl |
filter: drop-shadow() |
backdrop-blur-*, backdrop-brightness-*, backdrop-contrast-*, backdrop-grayscale, backdrop-hue-rotate-*, backdrop-invert, backdrop-opacity-*, backdrop-saturate-*, backdrop-sepia |
backdrop-filter |
Tables
| Classes |
CSS Property |
border-collapse, border-separate |
border-collapse |
border-spacing-*, border-spacing-x-*, border-spacing-y-* |
border-spacing |
table-auto, table-fixed |
table-layout |
caption-top, caption-bottom |
caption-side |
Transitions & Animations
| Classes |
CSS Property |
transition (default), transition-all, transition-colors, transition-opacity, transition-shadow, transition-transform, transition-none |
transition-property |
transition-normal, transition-discrete |
transition-behavior |
duration-0, duration-75, duration-100, duration-150, duration-200, duration-300, duration-500, duration-700, duration-1000 |
transition-duration |
ease-linear, ease-in, ease-out, ease-in-out |
transition-timing-function |
delay-* (same as duration values) |
transition-delay |
animate-spin, animate-ping, animate-pulse, animate-bounce, animate-none |
animation |
Transforms
| Classes |
CSS Property |
rotate-0, rotate-1, rotate-2, rotate-3, rotate-6, rotate-12, rotate-45, rotate-90, rotate-180, -rotate-*, rotate-x-*, rotate-y-* |
rotate |
scale-0, scale-50, scale-75, scale-90, scale-95, scale-100, scale-105, scale-110, scale-125, scale-150, scale-200, scale-x-*, scale-y-* |
scale |
skew-x-*, skew-y-* |
skew |
translate-x-*, translate-y-*, translate-z-* (spacing scale + fractions) |
translate |
origin-center, origin-top, origin-top-right, etc. |
transform-origin |
transform-gpu, transform-none |
transform |
transform-flat, transform-3d |
transform-style |
backface-visible, backface-hidden |
backface-visibility |
perspective-none, perspective-near–perspective-distant |
perspective |
perspective-origin-* |
perspective-origin |
Interactivity
| Classes |
CSS Property |
accent-* (colors), accent-auto |
accent-color |
appearance-none, appearance-auto |
appearance |
caret-* (colors) |
caret-color |
color-scheme-normal, color-scheme-dark, color-scheme-light, color-scheme-light-dark |
color-scheme |
cursor-auto, cursor-default, cursor-pointer, cursor-wait, cursor-text, cursor-move, cursor-help, cursor-not-allowed, cursor-none, cursor-grab, cursor-grabbing, cursor-col-resize, cursor-row-resize, cursor-zoom-in, cursor-zoom-out, etc. |
cursor |
field-sizing-content, field-sizing-fixed |
field-sizing (new in v4) |
pointer-events-none, pointer-events-auto |
pointer-events |
resize-none, resize, resize-x, resize-y |
resize |
scroll-auto, scroll-smooth |
scroll-behavior |
scroll-m-*, scroll-p-* (all sides) |
scroll-margin, scroll-padding |
snap-start, snap-end, snap-center, snap-align-none |
scroll-snap-align |
snap-normal, snap-always |
scroll-snap-stop |
snap-none, snap-x, snap-y, snap-both, snap-mandatory, snap-proximity |
scroll-snap-type |
touch-auto, touch-none, touch-pan-x, touch-pan-y, touch-pinch-zoom, touch-manipulation |
touch-action |
select-none, select-text, select-all, select-auto |
user-select |
will-change-auto, will-change-scroll, will-change-contents, will-change-transform |
will-change |
SVG
| Classes |
CSS Property |
fill-none, fill-current, fill-* (colors) |
fill |
stroke-none, stroke-current, stroke-* (colors) |
stroke |
stroke-0, stroke-1, stroke-2 |
stroke-width |
Accessibility
| Classes |
Effect |
sr-only |
Screen-reader only (visually hidden, accessible) |
not-sr-only |
Reset sr-only |
forced-color-adjust-auto |
forced-color-adjust: auto |
forced-color-adjust-none |
forced-color-adjust: none |
Source Detection Rules
Never construct class names dynamically:
// ❌ WRONG — Tailwind cannot detect these
<div className={`bg-${color}-500`} />
<div className={`text-${size}`} />
// ✅ CORRECT — use complete class name strings
const colorMap = {
red: "bg-red-500 text-white",
blue: "bg-blue-500 text-white",
green: "bg-green-500 text-white",
};
<div className={colorMap[color]} />
// ✅ CORRECT — ternary with complete strings
<div className={isActive ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700"} />
Component Patterns
Responsive Card
<div class="mx-auto max-w-sm rounded-xl bg-white p-6 shadow-lg
sm:max-w-md sm:p-8
dark:bg-gray-800 dark:shadow-none
dark:ring-1 dark:ring-white/10">
<h2 class="text-xl font-bold text-gray-900 dark:text-white">Title</h2>
<p class="mt-2 text-gray-600 dark:text-gray-400">Description.</p>
<button class="mt-4 rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white
hover:bg-blue-700 active:bg-blue-800
focus:outline-2 focus:outline-offset-2 focus:outline-blue-500
disabled:opacity-50 disabled:cursor-not-allowed
transition-colors">
Action
</button>
</div>
Navigation Bar
<nav class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200
dark:bg-gray-900/80 dark:border-gray-800">
<div class="mx-auto flex max-w-7xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
<a href="/" class="text-xl font-bold text-gray-900 dark:text-white">Logo</a>
<div class="hidden gap-6 sm:flex">
<a href="#" class="text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white transition-colors">Home</a>
<a href="#" class="text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white transition-colors">About</a>
</div>
<button class="sm:hidden rounded-md p-2 hover:bg-gray-100 dark:hover:bg-gray-800">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</nav>
Form with Validation States
<form class="mx-auto max-w-md space-y-6 rounded-xl bg-white p-8 shadow-lg dark:bg-gray-800">
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300" for="email">Email</label>
<input type="email" id="email" required
class="w-full rounded-lg border border-gray-300 px-4 py-2
focus:border-blue-500 focus:ring-2 focus:ring-blue-200 focus:outline-none
invalid:border-red-500 focus:invalid:ring-red-200
disabled:bg-gray-100 disabled:cursor-not-allowed
placeholder:text-gray-400
dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder:text-gray-500"
placeholder="you@example.com" />
</div>
<button type="submit"
class="w-full rounded-lg bg-blue-600 py-3 font-bold text-white
hover:bg-blue-700 active:bg-blue-800 transition-colors
disabled:opacity-50 disabled:cursor-not-allowed">
Sign In
</button>
</form>
Responsive Grid
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{items.map(item => (
<div class="rounded-lg bg-white p-6 shadow-md hover:shadow-lg transition-shadow
dark:bg-gray-800">
{item.name}
</div>
))}
</div>
Best Practices
- Use
@theme for all design tokens — don't scatter arbitrary values; define them as theme variables
- Mobile-first always — base styles are mobile, add
sm:, md:, lg: for larger screens
- Dark mode from the start — add
dark: variants as you go, not as an afterthought
- Complete class strings only — never interpolate partial class names
- Prefer utility classes over
@apply — extract components in your framework, not in CSS
- Use semantic color names —
--color-primary, --color-danger over raw palette references
- Keep spacing consistent — use the spacing scale, reach for arbitrary values sparingly
- Accessibility always — add
focus:, focus-visible:, sr-only, forced-color-adjust as needed
- Use
@layer components for complex reusable styles — maintains proper specificity ordering
- Use container queries for truly portable components that respond to their parent, not the viewport
1---2name: tailwindcss3description: Tailwind CSS v4 utility-first styling with CSS-native configuration. Use when styling with Tailwind utility classes, configuring themes via @theme, building responsive/dark-mode layouts, creating custom utilities or variants, or working with container queries. Covers the complete v4 API including @theme, @utility, @custom-variant, @variant, @source, and all utility classes.4---56# Tailwind CSS v4 — Claude Code Skill78> **Tailwind CSS v4** (released January 2025, current stable v4.2) uses a **CSS-first9> configuration** approach. This skill covers v4 exclusively. All theme customization10> happens in CSS via `@theme`, not in `tailwind.config.js`.1112## Critical: v3 → v4 Breaking Changes1314**Do NOT use these v3 patterns — they are wrong for v4:**1516| ❌ v3 (WRONG) | ✅ v4 (CORRECT) |17|---|---|18| `@tailwind base; @tailwind components; @tailwind utilities;` | `@import "tailwindcss";` |19| `tailwind.config.js` theme/extend | `@theme { --color-brand: ...; }` in CSS |20| `content: ['./src/**/*.{js,tsx}']` in JS config | Automatic detection + `@source` directive |21| `theme()` function in CSS | `var(--color-red-500)` CSS variables |22| `!bg-red-500` (prefix `!` for important) | `bg-red-500!` (suffix `!`) |23| `darkMode: 'class'` in JS config | `@custom-variant dark (&:where(.dark, .dark *));` |24| `addUtility()` in plugin JS | `@utility name { ... }` in CSS |25| `addVariant()` in plugin JS | `@custom-variant name (selector);` in CSS |26| `@screen md { ... }` | `@variant md { ... }` or just use `md:` prefix |27| `@apply` in Vue/Svelte `<style>` (breaks) | Add `@reference "../../app.css";` first |28| `require('@tailwindcss/container-queries')` | Built-in: `@container` + `@sm:` / `@md:` etc. |29| `bg-[var(--my-color)]` | `bg-(--my-color)` (v4 shorthand) |3031## Setup3233### CSS Entry Point3435```css36/* app.css — the single import replaces all v3 @tailwind directives */37@import "tailwindcss";3839/* Optional modifiers: */40@import "tailwindcss" prefix(tw); /* Prefix all classes: tw:flex */41@import "tailwindcss" important; /* All utilities get !important */42@import "tailwindcss" source("../src"); /* Set source scanning root */43```4445### Vite Setup4647```bash48npm install -D tailwindcss @tailwindcss/vite49```5051```javascript52// vite.config.ts53import tailwindcss from '@tailwindcss/vite'54import { defineConfig } from 'vite'5556export default defineConfig({57 plugins: [tailwindcss()],58})59```6061### PostCSS Setup (Alternative)6263```bash64npm install -D tailwindcss @tailwindcss/postcss autoprefixer65```6667```javascript68// postcss.config.js69export default {70 plugins: {71 "@tailwindcss/postcss": {},72 autoprefixer: {},73 },74}75```7677### CLI Setup (No Bundler)7879```bash80npm install -D tailwindcss @tailwindcss/cli81npx @tailwindcss/cli -i app.css -o dist/app.css --watch82```8384## Reference Material8586A comprehensive Tailwind CSS v4.2 reference document is available at:87```88../../guides/tailwindcss-v4/reference.md89```9091(Relative to this SKILL.md file.)9293**When to consult the reference:**94- You need the exact list of values for a specific utility (e.g., all `blur-*` sizes, all `cursor-*` options)95- You're unsure whether a utility exists in v4 or was removed/renamed from v396- You need the precise `@theme` namespace for a category (e.g., which variable prefix generates which utilities)97- You want to verify the correct syntax for a v4 directive (`@utility`, `@custom-variant`, `@source`, `@variant`)98- You encounter an unfamiliar v4 feature (container queries, `field-sizing`, `text-shadow`, mask utilities, `@reference`)99100**When you don't need it:**101- Standard layout, spacing, typography, color, and responsive patterns are covered in this SKILL.md102- Common component patterns (cards, navs, forms, grids) are already here103- The v3→v4 migration table at the top of this file covers the most common pitfalls104105Read the reference with the `view` tool if you need detail beyond what this skill file provides.106107## Theme System (`@theme`)108109Theme variables are special CSS variables defined with `@theme` that **create110corresponding utility classes**. They are not just CSS variables — they instruct111Tailwind to generate utilities.112113### Defining Theme Variables114115```css116@import "tailwindcss";117118@theme {119 /* Colors → bg-brand, text-brand, border-brand, ring-brand, etc. */120 --color-brand-50: oklch(0.97 0.02 264);121 --color-brand-500: oklch(0.55 0.22 264);122 --color-brand-900: oklch(0.25 0.15 264);123124 /* Fonts → font-display, font-body utilities */125 --font-display: "Satoshi", "Inter", sans-serif;126 --font-body: "Inter", system-ui, sans-serif;127128 /* Font sizes → text-display utility */129 --text-display: 3rem;130131 /* Font weights → font-heading utility */132 --font-weight-heading: 700;133134 /* Letter spacing → tracking-wide utility */135 --tracking-wide: 0.025em;136137 /* Line height → leading-relaxed utility */138 --leading-relaxed: 1.75;139140 /* Breakpoints → 3xl: responsive variant */141 --breakpoint-3xl: 120rem;142143 /* Container sizes → @8xl: container query variant + max-w-8xl */144 --container-8xl: 96rem;145146 /* Border radius → rounded-xl utility */147 --radius-xl: 0.75rem;148149 /* Shadows → shadow-soft utility */150 --shadow-soft: 0 2px 8px rgba(0, 0, 0, 0.08);151152 /* Inset shadows → inset-shadow-deep utility */153 --inset-shadow-deep: inset 0 4px 8px rgba(0, 0, 0, 0.15);154155 /* Drop shadows → drop-shadow-hard utility */156 --drop-shadow-hard: 0 2px 4px rgba(0, 0, 0, 0.3);157158 /* Blur → blur-heavy utility */159 --blur-heavy: 40px;160161 /* Perspective → perspective-dramatic utility */162 --perspective-dramatic: 200px;163164 /* Aspect ratio → aspect-golden utility */165 --aspect-golden: 1.618 / 1;166167 /* Easing → ease-snappy utility */168 --ease-snappy: cubic-bezier(0.2, 0, 0, 1);169170 /* Animations → animate-fade-in utility */171 --animate-fade-in: fade-in 0.3s ease-out;172 @keyframes fade-in {173 from { opacity: 0; }174 to { opacity: 1; }175 }176}177```178179### Namespace → Utility Mapping180181| Theme Namespace | Creates |182|---|---|183| `--color-*` | `bg-*`, `text-*`, `border-*`, `ring-*`, `fill-*`, `stroke-*`, `accent-*`, `caret-*`, `outline-*`, `decoration-*`, `shadow-*` (color), `from-*`, `via-*`, `to-*` |184| `--font-*` | `font-*` (family) |185| `--text-*` | `text-*` (size) |186| `--font-weight-*` | `font-*` (weight) |187| `--tracking-*` | `tracking-*` (letter-spacing) |188| `--leading-*` | `leading-*` (line-height) |189| `--breakpoint-*` | `sm:`, `md:`, `lg:`, etc. responsive variants |190| `--container-*` | `@sm:`, `@md:`, etc. container query variants + `max-w-*` |191| `--spacing-*` | `p-*`, `m-*`, `gap-*`, `w-*`, `h-*`, and all spacing utilities |192| `--radius-*` | `rounded-*` |193| `--shadow-*` | `shadow-*` |194| `--inset-shadow-*` | `inset-shadow-*` |195| `--drop-shadow-*` | `drop-shadow-*` |196| `--blur-*` | `blur-*` |197| `--perspective-*` | `perspective-*` |198| `--aspect-*` | `aspect-*` |199| `--ease-*` | `ease-*` |200| `--animate-*` | `animate-*` |201202### Overriding & Removing Defaults203204```css205@theme {206 /* Override one value */207 --breakpoint-sm: 30rem;208209 /* Remove entire namespace, then redefine */210 --color-*: initial;211 --color-white: #fff;212 --color-black: #000;213 --color-primary: oklch(0.6 0.2 260);214215 /* Remove a single default */216 --breakpoint-2xl: initial;217218 /* Reset everything (for fully custom themes) */219 --*: initial;220}221```222223### Referencing Other Variables (use `inline`)224225```css226@theme inline {227 --font-sans: var(--font-inter);228}229```230231The `inline` option inlines the *value* rather than referencing the theme variable,232avoiding CSS variable resolution scope issues.233234## CSS Directives235236### `@source` — Register Additional Scan Paths237238```css239@source "../node_modules/@my-company/ui-lib";240@source "../../packages/shared-components";241```242243### `@source not()` — Ignore Paths244245```css246@source not("./src/legacy");247```248249### `@source inline()` — Safelist Classes250251```css252@source inline("underline bg-red-500 lg:flex");253```254255### `@utility` — Custom Utilities256257```css258/* Simple utility */259@utility content-auto {260 content-visibility: auto;261}262263/* With nesting */264@utility scrollbar-hidden {265 &::-webkit-scrollbar {266 display: none;267 }268}269270/* Functional utility (accepts values) */271@utility tab-* {272 tab-size: --value(--tab-size-*); /* Match theme keys */273}274275@utility tab-* {276 tab-size: --value(integer); /* Bare integer values: tab-4 */277}278279/* Multiple resolvers (first match wins) */280@utility tab-* {281 tab-size: --value(--tab-size-*, integer, "inherit", "initial");282}283```284285### `@custom-variant` — Custom Variants286287```css288/* Class-based dark mode (replaces v3 darkMode: 'class') */289@custom-variant dark (&:where(.dark, .dark *));290291/* Data attribute dark mode */292@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));293294/* Custom theme variant */295@custom-variant theme-midnight (&:where([data-theme="midnight"] *));296```297298### `@variant` — Apply Variants in Custom CSS299300```css301.my-element {302 background: white;303 @variant dark {304 background: black;305 }306 @variant hover {307 @variant dark {308 background: #333;309 }310 }311}312```313314### `@layer` — Custom CSS in Layers315316```css317@layer base {318 h1 { font-size: var(--text-2xl); }319 h2 { font-size: var(--text-xl); }320}321322@layer components {323 .card {324 background-color: var(--color-white);325 border-radius: var(--radius-lg);326 padding: calc(var(--spacing) * 6);327 box-shadow: var(--shadow-xl);328 }329 .btn-primary {330 @apply rounded-lg bg-blue-600 px-4 py-2 font-semibold text-white;331 @apply hover:bg-blue-700 active:bg-blue-800;332 @apply focus:outline-2 focus:outline-offset-2 focus:outline-blue-500;333 }334}335```336337### `@reference` — For Vue/Svelte `<style>` Blocks338339```html340<style>341 @reference "../../app.css";342 h1 {343 @apply text-2xl font-bold text-red-500;344 }345</style>346```347348### `@apply` — Inline Utility Classes in CSS349350```css351.select2-dropdown {352 @apply rounded-b-lg shadow-md;353}354```355356### Legacy Compatibility357358```css359@config "../../tailwind.config.js"; /* Load v3 JS config */360@plugin "@tailwindcss/typography"; /* Load JS plugin */361```362363## CSS Functions364365```css366/* Adjust color opacity at build time */367.element { color: --alpha(var(--color-lime-300) / 50%); }368369/* Generate spacing from theme multiplier */370.element { margin: --spacing(4); } /* = calc(var(--spacing) * 4) */371372/* In arbitrary values: */373<div class="py-[calc(--spacing(4)-1px)]">374```375376## Responsive Design377378### Default Breakpoints (Mobile-First)379380| Prefix | Min Width | CSS |381|---|---|---|382| *(none)* | 0 | Base styles — apply to ALL sizes |383| `sm:` | 40rem (640px) | `@media (width >= 40rem)` |384| `md:` | 48rem (768px) | `@media (width >= 48rem)` |385| `lg:` | 64rem (1024px) | `@media (width >= 64rem)` |386| `xl:` | 80rem (1280px) | `@media (width >= 80rem)` |387| `2xl:` | 96rem (1536px) | `@media (width >= 96rem)` |388389### Max-Width & Range Queries390391```html392<div class="max-md:flex"> <!-- Below 768px only -->393<div class="md:max-xl:flex"> <!-- Between 768px and 1280px -->394<div class="min-[900px]:flex"> <!-- Arbitrary min-width -->395<div class="max-[600px]:hidden"> <!-- Arbitrary max-width -->396```397398### Mobile-First Pattern399400```html401<!-- CORRECT: style mobile first, override upward -->402<div class="text-center sm:text-left">403404<!-- WRONG: don't use sm: to "target mobile" -->405<div class="sm:text-center"> <!-- This only applies at 640px+ -->406```407408### Responsive Example409410```html411<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">412 <div class="rounded-lg bg-white p-4 shadow">Item</div>413 <!-- ... -->414</div>415416<h1 class="text-2xl font-bold md:text-4xl lg:text-6xl">417 Responsive heading418</h1>419420<div class="hidden lg:block">Desktop only</div>421<div class="lg:hidden">Mobile/tablet only</div>422```423424## Container Queries (Built-In)425426```html427<!-- Mark parent as container -->428<div class="@container">429 <!-- Style based on container width, not viewport -->430 <div class="flex flex-col @md:flex-row @md:items-center gap-4">431 <img class="w-full @md:w-48 rounded-lg" src="..." alt="">432 <div>433 <h3 class="text-lg font-bold">Title</h3>434 <p class="text-sm text-gray-500">Description</p>435 </div>436 </div>437</div>438439<!-- Named containers for nested queries -->440<div class="@container/main">441 <div class="@sm/main:flex-row flex flex-col">...</div>442</div>443444<!-- Max-width container queries -->445<div class="@container">446 <div class="flex flex-row @max-md:flex-col">...</div>447</div>448449<!-- Range queries -->450<div class="@container">451 <div class="@sm:@max-md:flex-col">Between sm and md container</div>452</div>453```454455## Dark Mode456457### Automatic (System Preference — Default)458459```html460<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">461 Switches with OS setting462</div>463```464465### Manual Toggle (Class-Based)466467```css468/* In your CSS */469@import "tailwindcss";470@custom-variant dark (&:where(.dark, .dark *));471```472473```html474<html class="dark">475 <body>476 <div class="bg-white dark:bg-gray-900">Now class-controlled</div>477 </body>478</html>479```480481```javascript482// Toggle in JS483document.documentElement.classList.toggle('dark',484 localStorage.theme === 'dark' ||485 (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)486);487```488489### Data Attribute Approach490491```css492@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));493```494495```html496<html data-theme="dark">...</html>497```498499## State Variants500501### Interactive States502503```html504<button class="bg-blue-600 hover:bg-blue-700 active:bg-blue-800505 focus:outline-2 focus:outline-offset-2 focus:outline-blue-500506 disabled:opacity-50 disabled:cursor-not-allowed507 transition-colors">508 Button509</button>510511<a class="text-blue-600 hover:text-blue-800 hover:underline512 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500513 visited:text-purple-600">514 Link515</a>516```517518### Form States519520```html521<input class="border border-gray-300 rounded-lg px-3 py-2522 focus:border-blue-500 focus:ring-2 focus:ring-blue-200523 invalid:border-red-500 focus:invalid:ring-red-200524 disabled:bg-gray-100 disabled:cursor-not-allowed525 placeholder:text-gray-400 placeholder:italic526 read-only:bg-gray-50"527 required />528529<input type="checkbox" class="checked:bg-blue-500 indeterminate:bg-gray-300" />530```531532### Structural (Children)533534```html535<ul>536 {items.map(item => (537 <li class="py-4 first:pt-0 last:pb-0 odd:bg-gray-50 even:bg-white">538 {item.name}539 </li>540 ))}541</ul>542543<div class="nth-3:underline">3rd child underlined</div>544<div class="empty:hidden">Hidden when empty</div>545```546547### Group (Parent State)548549```html550<a href="#" class="group block rounded-lg p-6 hover:bg-gray-50">551 <h3 class="font-semibold group-hover:text-blue-600">Title</h3>552 <p class="text-gray-500 group-hover:text-gray-700">Description</p>553 <svg class="hidden group-has-[a]:block">...</svg>554</a>555556<!-- Named groups for nesting -->557<div class="group/card">558 <div class="group/button">559 <span class="group-hover/card:text-blue-500560 group-hover/button:underline">Text</span>561 </div>562</div>563```564565### Peer (Sibling State)566567```html568<label class="block">569 <input type="email" class="peer rounded border px-3 py-2" required />570 <p class="invisible mt-1 text-sm text-red-500 peer-invalid:visible">571 Please enter a valid email.572 </p>573</label>574575<!-- Named peers -->576<input id="draft" class="peer/draft" type="radio" name="status" checked />577<div class="hidden peer-checked/draft:block">Draft selected</div>578```579580### Implicit Group (`in-*`)581582```html583<!-- No need to add "group" class to parent -->584<div tabindex="0">585 <div class="opacity-50 in-focus:opacity-100">586 Responds to any parent's focus587 </div>588</div>589```590591### Has (Descendant State)592593```html594<label class="has-checked:bg-indigo-50 has-checked:ring-indigo-200">595 <input type="radio" class="checked:border-indigo-500" />596 Google Pay597</label>598```599600### Not601602```html603<button class="bg-indigo-600 hover:not-focus:bg-indigo-700">604 Only hover styles when NOT focused605</button>606```607608### Pseudo-Elements609610```html611<div class="before:content-[''] before:absolute before:inset-0 before:bg-black/50">612 Overlay613</div>614<blockquote class="after:content-[attr(data-cite)]">Quote</blockquote>615<input class="placeholder:text-gray-400" placeholder="Search..." />616<li class="marker:text-blue-500">List item</li>617<p class="selection:bg-yellow-200">Selectable text</p>618<input type="file" class="file:mr-4 file:rounded-lg file:border-0 file:bg-blue-50 file:px-4 file:py-2" />619```620621### Media & Feature Queries622623```html624<div class="motion-safe:animate-bounce">Respects reduced motion</div>625<div class="motion-reduce:transition-none">No transitions if reduced motion</div>626<div class="contrast-more:border-2">Higher contrast borders</div>627<div class="print:hidden">Hidden when printing</div>628<div class="portrait:flex-col landscape:flex-row">Orientation-aware</div>629<div class="supports-[display:grid]:grid">Feature detection</div>630<div class="forced-colors:border">Forced colors mode</div>631```632633### Data & ARIA Attributes634635```html636<div class="data-[state=open]:bg-blue-50" data-state="open">Open</div>637<div class="aria-expanded:rotate-180" aria-expanded="true">Chevron</div>638<div class="aria-busy:opacity-50" aria-busy="true">Loading</div>639<details class="open:bg-gray-50" open>Expandable</details>640```641642### Stacking Variants643644```html645<button class="dark:md:hover:bg-indigo-600">646 Dark mode + medium screen + hover647</button>648649<div class="dark:lg:data-[active]:hover:bg-fuchsia-600">650 Four stacked variants651</div>652```653654### Arbitrary Variants655656```html657<div class="[&>p]:mt-4">Direct child p tags</div>658<div class="[&_p]:mt-4">Any descendant p tags</div>659<div class="lg:[&:nth-child(-n+3)]:hover:underline">Complex selector</div>660<div class="[@supports(display:grid)]:grid">@supports</div>661```662663## Spacing System664665In v4, spacing uses a single `--spacing` multiplier (default `0.25rem`):666667```668p-0 → padding: 0669p-px → padding: 1px670p-0.5 → padding: calc(var(--spacing) * 0.5) = 0.125rem671p-1 → padding: calc(var(--spacing) * 1) = 0.25rem672p-2 → padding: calc(var(--spacing) * 2) = 0.5rem673p-4 → padding: calc(var(--spacing) * 4) = 1rem674p-8 → padding: calc(var(--spacing) * 8) = 2rem675p-16 → 4rem, p-24 → 6rem, p-32 → 8rem, p-64 → 16rem, p-96 → 24rem676```677678Any number works: `p-3.5`, `p-7`, `p-18`, `p-72`. The same scale applies to679margin, gap, width, height, inset, translate, scroll-margin, scroll-padding,680space-between, text-indent, border-spacing, and all spacing-based utilities.681682## Arbitrary Values683684```html685<div class="top-[117px]"> <!-- Any CSS value -->686<div class="bg-[#bada55]"> <!-- Hex color -->687<div class="text-[22px]"> <!-- Font size -->688<div class="grid-cols-[1fr_500px_2fr]"> <!-- Underscores = spaces -->689<div class="bg-[url('/img.png')]"> <!-- URLs -->690<div class="before:content-['Hello']"> <!-- Content strings -->691<div class="max-h-[calc(100dvh-4rem)]"> <!-- calc() -->692<div class="[mask-type:luminance]"> <!-- Arbitrary CSS property -->693<div class="[--my-var:1rem]"> <!-- CSS variable -->694695<!-- CSS variable shorthand (v4): adds var() automatically -->696<div class="fill-(--my-brand-color)"> <!-- = fill-[var(--my-brand-color)] -->697698<!-- Type hints for ambiguous variables -->699<div class="text-(length:--my-var)"> <!-- Force font-size interpretation -->700<div class="text-(color:--my-var)"> <!-- Force color interpretation -->701```702703### Important Modifier704705```html706<!-- v4: suffix ! -->707<div class="bg-red-500!">708709<!-- v3 (WRONG in v4): prefix ! -->710<!-- <div class="!bg-red-500"> -->711```712713### Opacity Modifier714715```html716<div class="bg-red-500/50"> <!-- 50% opacity -->717<div class="bg-red-500/[.15]"> <!-- Arbitrary opacity -->718<div class="text-black/75"> <!-- 75% opacity text -->719```720721### Negative Values722723```html724<div class="-mt-4"> <!-- Negative margin -->725<div class="-translate-x-1/2"> <!-- Negative translate -->726<div class="-rotate-45"> <!-- Negative rotation -->727```728729## Color System730731### Default Palette73273322 color families, each with shades 50–950:734`slate`, `gray`, `zinc`, `neutral`, `stone`, `red`, `orange`, `amber`,735`yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`,736`indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`.737738Plus: `black`, `white`, `transparent`, `current`, `inherit`.739740All default colors use **oklch** color space.741742### Custom Colors743744```css745@theme {746 --color-brand-50: oklch(0.97 0.02 264);747 --color-brand-500: oklch(0.55 0.22 264);748 --color-brand-900: oklch(0.25 0.15 264);749}750```751752Now `bg-brand-500`, `text-brand-50`, `border-brand-900`, etc. all work.753754## Complete Utility Reference755756### Layout757758| Classes | CSS Property |759|---|---|760| `block`, `inline-block`, `inline`, `flex`, `inline-flex`, `grid`, `inline-grid`, `table`, `contents`, `flow-root`, `list-item`, `hidden` | `display` |761| `aspect-auto`, `aspect-square`, `aspect-video`, `aspect-[4/3]` | `aspect-ratio` |762| `columns-1`–`columns-12`, `columns-auto`, `columns-3xs`–`columns-7xl` | `columns` |763| `box-border`, `box-content` | `box-sizing` |764| `float-start`, `float-end`, `float-right`, `float-left`, `float-none` | `float` |765| `clear-start`, `clear-end`, `clear-both`, `clear-none` | `clear` |766| `isolate`, `isolation-auto` | `isolation` |767| `object-contain`, `object-cover`, `object-fill`, `object-none`, `object-scale-down` | `object-fit` |768| `object-bottom`, `object-center`, `object-left`, `object-top`, etc. | `object-position` |769| `overflow-auto`, `overflow-hidden`, `overflow-clip`, `overflow-visible`, `overflow-scroll`, `overflow-x-*`, `overflow-y-*` | `overflow` |770| `overscroll-auto`, `overscroll-contain`, `overscroll-none` | `overscroll-behavior` |771| `static`, `fixed`, `absolute`, `relative`, `sticky` | `position` |772| `inset-*`, `inset-x-*`, `inset-y-*`, `top-*`, `right-*`, `bottom-*`, `left-*`, `start-*`, `end-*` | `inset` / TRBL |773| `visible`, `invisible`, `collapse` | `visibility` |774| `z-0`, `z-10`, `z-20`, `z-30`, `z-40`, `z-50`, `z-auto` | `z-index` |775776### Flexbox & Grid777778| Classes | CSS Property |779|---|---|780| `flex-row`, `flex-row-reverse`, `flex-col`, `flex-col-reverse` | `flex-direction` |781| `flex-wrap`, `flex-wrap-reverse`, `flex-nowrap` | `flex-wrap` |782| `flex-1`, `flex-auto`, `flex-initial`, `flex-none` | `flex` shorthand |783| `grow`, `grow-0` | `flex-grow` |784| `shrink`, `shrink-0` | `flex-shrink` |785| `basis-*` (numbers, `auto`, `full`, fractions) | `flex-basis` |786| `order-*` (1–12, `first`, `last`, `none`) | `order` |787| `grid-cols-*` (1–12, `none`, `subgrid`) | `grid-template-columns` |788| `grid-rows-*` (1–12, `none`, `subgrid`) | `grid-template-rows` |789| `col-span-*`, `col-start-*`, `col-end-*` | `grid-column` |790| `row-span-*`, `row-start-*`, `row-end-*` | `grid-row` |791| `grid-flow-row`, `grid-flow-col`, `grid-flow-dense` | `grid-auto-flow` |792| `auto-cols-auto`, `auto-cols-min`, `auto-cols-max`, `auto-cols-fr` | `grid-auto-columns` |793| `auto-rows-auto`, `auto-rows-min`, `auto-rows-max`, `auto-rows-fr` | `grid-auto-rows` |794| `gap-*`, `gap-x-*`, `gap-y-*` | `gap` |795| `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`, `justify-evenly` | `justify-content` |796| `justify-items-start`, `justify-items-end`, `justify-items-center`, `justify-items-stretch` | `justify-items` |797| `justify-self-auto`, `justify-self-start`, `justify-self-end`, `justify-self-center` | `justify-self` |798| `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch` | `align-items` |799| `content-center`, `content-start`, `content-end`, `content-between`, `content-around` | `align-content` |800| `self-auto`, `self-start`, `self-end`, `self-center`, `self-stretch` | `align-self` |801| `place-content-*`, `place-items-*`, `place-self-*` | Place shorthands |802803### Sizing804805| Classes | CSS Property |806|---|---|807| `w-*` (numbers, `auto`, `full`, `screen`, `dvw`, `svw`, `lvw`, `min`, `max`, `fit`, fractions) | `width` |808| `h-*` (numbers, `auto`, `full`, `screen`, `dvh`, `svh`, `lvh`, `min`, `max`, `fit`) | `height` |809| `size-*` (sets both width and height) | `width` + `height` |810| `min-w-*`, `max-w-*` (`none`, `full`, `min`, `max`, `fit`, `prose`, `screen-*`, container sizes) | `min/max-width` |811| `min-h-*`, `max-h-*` | `min/max-height` |812813### Typography814815| Classes | CSS Property |816|---|---|817| `font-sans`, `font-serif`, `font-mono` | `font-family` |818| `text-xs`–`text-9xl`, `text-sm/6` (combined size/line-height) | `font-size` |819| `font-thin`(100)–`font-black`(900) | `font-weight` |820| `italic`, `not-italic` | `font-style` |821| `antialiased`, `subpixel-antialiased` | `font-smoothing` |822| `tracking-tighter`–`tracking-widest` | `letter-spacing` |823| `leading-none`(1)–`leading-loose`(2) | `line-height` |824| `text-left`, `text-center`, `text-right`, `text-justify`, `text-start`, `text-end` | `text-align` |825| `text-*` (colors: `text-gray-900`, `text-blue-500/50`) | `color` |826| `underline`, `overline`, `line-through`, `no-underline` | `text-decoration-line` |827| `decoration-*` (color, style, thickness) | `text-decoration-*` |828| `uppercase`, `lowercase`, `capitalize`, `normal-case` | `text-transform` |829| `truncate`, `text-ellipsis`, `text-clip` | `text-overflow` |830| `text-wrap`, `text-nowrap`, `text-balance`, `text-pretty` | `text-wrap` |831| `line-clamp-1`–`line-clamp-6`, `line-clamp-none` | Line clamping |832| `whitespace-normal`, `whitespace-nowrap`, `whitespace-pre`, `whitespace-pre-line`, `whitespace-pre-wrap` | `white-space` |833| `break-normal`, `break-words`, `break-all`, `break-keep` | `word-break` |834| `list-none`, `list-disc`, `list-decimal`, `list-inside`, `list-outside` | `list-style` |835| `tabular-nums`, `oldstyle-nums`, `lining-nums`, `proportional-nums`, `slashed-zero`, `ordinal`, `diagonal-fractions`, `stacked-fractions` | `font-variant-numeric` |836| `indent-*` | `text-indent` |837| `align-baseline`, `align-top`, `align-middle`, `align-bottom` | `vertical-align` |838| `content-none`, `content-[value]` | `content` |839840### Backgrounds841842| Classes | CSS Property |843|---|---|844| `bg-*` (colors, opacity: `bg-red-500/50`) | `background-color` |845| `bg-none`, `bg-linear-to-t/tr/r/br/b/bl/l/tl`, `bg-radial-*`, `bg-conic-*` | `background-image` |846| `from-*`, `via-*`, `to-*` (gradient stops with colors + positions) | Gradient color stops |847| `bg-fixed`, `bg-local`, `bg-scroll` | `background-attachment` |848| `bg-clip-border`, `bg-clip-padding`, `bg-clip-content`, `bg-clip-text` | `background-clip` |849| `bg-auto`, `bg-cover`, `bg-contain` | `background-size` |850| `bg-center`, `bg-top`, `bg-bottom`, `bg-left`, `bg-right`, etc. | `background-position` |851| `bg-repeat`, `bg-no-repeat`, `bg-repeat-x`, `bg-repeat-y` | `background-repeat` |852853### Borders854855| Classes | CSS Property |856|---|---|857| `rounded-none`–`rounded-full`, per-side: `rounded-t-*`, `rounded-tl-*`, `rounded-s-*`, `rounded-ss-*` | `border-radius` |858| `border`, `border-0`, `border-2`, `border-4`, `border-8`, per-side: `border-t-*`, `border-x-*`, `border-y-*` | `border-width` |859| `border-*` (colors), per-side: `border-t-red-500` | `border-color` |860| `border-solid`, `border-dashed`, `border-dotted`, `border-double`, `border-hidden`, `border-none` | `border-style` |861| `outline`, `outline-0`–`outline-8` | `outline-width` |862| `outline-*` (colors) | `outline-color` |863| `outline-none`, `outline-dashed`, `outline-dotted`, `outline-double` | `outline-style` |864| `outline-offset-*` (0, 1, 2, 4, 8) | `outline-offset` |865| `ring`, `ring-0`–`ring-8`, `ring-inset`, `ring-*` (color) | Ring (box-shadow) |866| `divide-x-*`, `divide-y-*`, `divide-*` (color/style), `divide-x-reverse` | Border between children |867868### Effects869870| Classes | CSS Property |871|---|---|872| `shadow-2xs`–`shadow-2xl`, `shadow-inner`, `shadow-none`, `shadow-*` (color) | `box-shadow` |873| `text-shadow-2xs`–`text-shadow-lg`, `text-shadow-none` | `text-shadow` (**new in v4**) |874| `opacity-0`–`opacity-100` | `opacity` |875| `mix-blend-*` (normal, multiply, screen, overlay, etc.) | `mix-blend-mode` |876| `bg-blend-*` | `background-blend-mode` |877| `mask-*` (clip, composite, image, mode, origin, position, repeat, size, type) | CSS masks (**new in v4**) |878| `inset-shadow-*` | Inset box shadows (**new in v4**) |879880### Filters881882| Classes | CSS Property |883|---|---|884| `blur-none`, `blur-xs`–`blur-3xl` | `filter: blur()` |885| `brightness-0`–`brightness-200` | `filter: brightness()` |886| `contrast-0`–`contrast-200` | `filter: contrast()` |887| `grayscale`, `grayscale-0` | `filter: grayscale()` |888| `hue-rotate-0`–`hue-rotate-180` | `filter: hue-rotate()` |889| `invert`, `invert-0` | `filter: invert()` |890| `saturate-0`–`saturate-200` | `filter: saturate()` |891| `sepia`, `sepia-0` | `filter: sepia()` |892| `drop-shadow-none`–`drop-shadow-2xl` | `filter: drop-shadow()` |893| `backdrop-blur-*`, `backdrop-brightness-*`, `backdrop-contrast-*`, `backdrop-grayscale`, `backdrop-hue-rotate-*`, `backdrop-invert`, `backdrop-opacity-*`, `backdrop-saturate-*`, `backdrop-sepia` | `backdrop-filter` |894895### Tables896897| Classes | CSS Property |898|---|---|899| `border-collapse`, `border-separate` | `border-collapse` |900| `border-spacing-*`, `border-spacing-x-*`, `border-spacing-y-*` | `border-spacing` |901| `table-auto`, `table-fixed` | `table-layout` |902| `caption-top`, `caption-bottom` | `caption-side` |903904### Transitions & Animations905906| Classes | CSS Property |907|---|---|908| `transition` (default), `transition-all`, `transition-colors`, `transition-opacity`, `transition-shadow`, `transition-transform`, `transition-none` | `transition-property` |909| `transition-normal`, `transition-discrete` | `transition-behavior` |910| `duration-0`, `duration-75`, `duration-100`, `duration-150`, `duration-200`, `duration-300`, `duration-500`, `duration-700`, `duration-1000` | `transition-duration` |911| `ease-linear`, `ease-in`, `ease-out`, `ease-in-out` | `transition-timing-function` |912| `delay-*` (same as duration values) | `transition-delay` |913| `animate-spin`, `animate-ping`, `animate-pulse`, `animate-bounce`, `animate-none` | `animation` |914915### Transforms916917| Classes | CSS Property |918|---|---|919| `rotate-0`, `rotate-1`, `rotate-2`, `rotate-3`, `rotate-6`, `rotate-12`, `rotate-45`, `rotate-90`, `rotate-180`, `-rotate-*`, `rotate-x-*`, `rotate-y-*` | `rotate` |920| `scale-0`, `scale-50`, `scale-75`, `scale-90`, `scale-95`, `scale-100`, `scale-105`, `scale-110`, `scale-125`, `scale-150`, `scale-200`, `scale-x-*`, `scale-y-*` | `scale` |921| `skew-x-*`, `skew-y-*` | `skew` |922| `translate-x-*`, `translate-y-*`, `translate-z-*` (spacing scale + fractions) | `translate` |923| `origin-center`, `origin-top`, `origin-top-right`, etc. | `transform-origin` |924| `transform-gpu`, `transform-none` | `transform` |925| `transform-flat`, `transform-3d` | `transform-style` |926| `backface-visible`, `backface-hidden` | `backface-visibility` |927| `perspective-none`, `perspective-near`–`perspective-distant` | `perspective` |928| `perspective-origin-*` | `perspective-origin` |929930### Interactivity931932| Classes | CSS Property |933|---|---|934| `accent-*` (colors), `accent-auto` | `accent-color` |935| `appearance-none`, `appearance-auto` | `appearance` |936| `caret-*` (colors) | `caret-color` |937| `color-scheme-normal`, `color-scheme-dark`, `color-scheme-light`, `color-scheme-light-dark` | `color-scheme` |938| `cursor-auto`, `cursor-default`, `cursor-pointer`, `cursor-wait`, `cursor-text`, `cursor-move`, `cursor-help`, `cursor-not-allowed`, `cursor-none`, `cursor-grab`, `cursor-grabbing`, `cursor-col-resize`, `cursor-row-resize`, `cursor-zoom-in`, `cursor-zoom-out`, etc. | `cursor` |939| `field-sizing-content`, `field-sizing-fixed` | `field-sizing` (**new in v4**) |940| `pointer-events-none`, `pointer-events-auto` | `pointer-events` |941| `resize-none`, `resize`, `resize-x`, `resize-y` | `resize` |942| `scroll-auto`, `scroll-smooth` | `scroll-behavior` |943| `scroll-m-*`, `scroll-p-*` (all sides) | `scroll-margin`, `scroll-padding` |944| `snap-start`, `snap-end`, `snap-center`, `snap-align-none` | `scroll-snap-align` |945| `snap-normal`, `snap-always` | `scroll-snap-stop` |946| `snap-none`, `snap-x`, `snap-y`, `snap-both`, `snap-mandatory`, `snap-proximity` | `scroll-snap-type` |947| `touch-auto`, `touch-none`, `touch-pan-x`, `touch-pan-y`, `touch-pinch-zoom`, `touch-manipulation` | `touch-action` |948| `select-none`, `select-text`, `select-all`, `select-auto` | `user-select` |949| `will-change-auto`, `will-change-scroll`, `will-change-contents`, `will-change-transform` | `will-change` |950951### SVG952953| Classes | CSS Property |954|---|---|955| `fill-none`, `fill-current`, `fill-*` (colors) | `fill` |956| `stroke-none`, `stroke-current`, `stroke-*` (colors) | `stroke` |957| `stroke-0`, `stroke-1`, `stroke-2` | `stroke-width` |958959### Accessibility960961| Classes | Effect |962|---|---|963| `sr-only` | Screen-reader only (visually hidden, accessible) |964| `not-sr-only` | Reset sr-only |965| `forced-color-adjust-auto` | `forced-color-adjust: auto` |966| `forced-color-adjust-none` | `forced-color-adjust: none` |967968## Source Detection Rules969970**Never construct class names dynamically:**971972```jsx973// ❌ WRONG — Tailwind cannot detect these974<div className={`bg-${color}-500`} />975<div className={`text-${size}`} />976977// ✅ CORRECT — use complete class name strings978const colorMap = {979 red: "bg-red-500 text-white",980 blue: "bg-blue-500 text-white",981 green: "bg-green-500 text-white",982};983<div className={colorMap[color]} />984985// ✅ CORRECT — ternary with complete strings986<div className={isActive ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-700"} />987```988989## Component Patterns990991### Responsive Card992993```html994<div class="mx-auto max-w-sm rounded-xl bg-white p-6 shadow-lg995 sm:max-w-md sm:p-8996 dark:bg-gray-800 dark:shadow-none997 dark:ring-1 dark:ring-white/10">998 <h2 class="text-xl font-bold text-gray-900 dark:text-white">Title</h2>999 <p class="mt-2 text-gray-600 dark:text-gray-400">Description.</p>1000 <button class="mt-4 rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white1001 hover:bg-blue-700 active:bg-blue-8001002 focus:outline-2 focus:outline-offset-2 focus:outline-blue-5001003 disabled:opacity-50 disabled:cursor-not-allowed1004 transition-colors">1005 Action1006 </button>1007</div>1008```10091010### Navigation Bar10111012```html1013<nav class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-2001014 dark:bg-gray-900/80 dark:border-gray-800">1015 <div class="mx-auto flex max-w-7xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">1016 <a href="/" class="text-xl font-bold text-gray-900 dark:text-white">Logo</a>1017 <div class="hidden gap-6 sm:flex">1018 <a href="#" class="text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white transition-colors">Home</a>1019 <a href="#" class="text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white transition-colors">About</a>1020 </div>1021 <button class="sm:hidden rounded-md p-2 hover:bg-gray-100 dark:hover:bg-gray-800">1022 <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">1023 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />1024 </svg>1025 </button>1026 </div>1027</nav>1028```10291030### Form with Validation States10311032```html1033<form class="mx-auto max-w-md space-y-6 rounded-xl bg-white p-8 shadow-lg dark:bg-gray-800">1034 <div>1035 <label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300" for="email">Email</label>1036 <input type="email" id="email" required1037 class="w-full rounded-lg border border-gray-300 px-4 py-21038 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 focus:outline-none1039 invalid:border-red-500 focus:invalid:ring-red-2001040 disabled:bg-gray-100 disabled:cursor-not-allowed1041 placeholder:text-gray-4001042 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder:text-gray-500"1043 placeholder="you@example.com" />1044 </div>1045 <button type="submit"1046 class="w-full rounded-lg bg-blue-600 py-3 font-bold text-white1047 hover:bg-blue-700 active:bg-blue-800 transition-colors1048 disabled:opacity-50 disabled:cursor-not-allowed">1049 Sign In1050 </button>1051</form>1052```10531054### Responsive Grid10551056```html1057<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">1058 {items.map(item => (1059 <div class="rounded-lg bg-white p-6 shadow-md hover:shadow-lg transition-shadow1060 dark:bg-gray-800">1061 {item.name}1062 </div>1063 ))}1064</div>1065```10661067## Best Practices106810691. **Use `@theme` for all design tokens** — don't scatter arbitrary values; define them as theme variables10702. **Mobile-first always** — base styles are mobile, add `sm:`, `md:`, `lg:` for larger screens10713. **Dark mode from the start** — add `dark:` variants as you go, not as an afterthought10724. **Complete class strings only** — never interpolate partial class names10735. **Prefer utility classes over `@apply`** — extract components in your framework, not in CSS10746. **Use semantic color names** — `--color-primary`, `--color-danger` over raw palette references10757. **Keep spacing consistent** — use the spacing scale, reach for arbitrary values sparingly10768. **Accessibility always** — add `focus:`, `focus-visible:`, `sr-only`, `forced-color-adjust` as needed10779. **Use `@layer components`** for complex reusable styles — maintains proper specificity ordering107810. **Use container queries** for truly portable components that respond to their parent, not the viewport