start-vibing-stacks 2.18.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/setup.js +11 -0
- package/package.json +1 -1
- package/stacks/_shared/skills/quality-gate/SKILL.md +11 -4
- package/stacks/frontend/react/skills/preline-ui/SKILL.md +6 -3
- package/stacks/frontend/react/skills/react-patterns/SKILL.md +125 -26
- package/stacks/frontend/react/skills/react-standards/SKILL.md +17 -4
- package/stacks/frontend/react/skills/react-ui-patterns/SKILL.md +106 -31
- package/stacks/frontend/react/skills/shadcn-ui/SKILL.md +284 -56
- package/stacks/frontend/react/skills/tailwind-patterns/SKILL.md +75 -16
- package/stacks/frontend/react/skills/zod-validation/SKILL.md +157 -35
- package/stacks/frontend/react-api/skills/axios-laravel-api/SKILL.md +2 -6
- package/stacks/frontend/react-api/skills/react-api-standards/SKILL.md +10 -12
- package/stacks/nodejs/scripts/check-route-slugs.mjs +130 -0
- package/stacks/nodejs/skills/nextjs-app-router/SKILL.md +222 -1
- package/stacks/nodejs/stack.json +2 -1
- package/stacks/nodejs/workflows/ci.yml +11 -0
|
@@ -1,97 +1,325 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: shadcn-ui
|
|
3
|
-
version:
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "shadcn/ui CLI v4 (March 2026) for React 19 + Tailwind v4. Components updated to remove forwardRef (refs as props in R19), every primitive carries a data-slot attribute for targeted styling, HSL → OKLCH tokens, size-* utility replaces w-h pairs, default style deprecated in favor of new-york. CLI v4 brings shadcn/skills (AI agent context for component registry), --preset (shareable design-system bundle), --dry-run / --diff / --view (inspect changes before applying), --template scaffolds (Next.js, Vite, Laravel, React Router, Astro, TanStack Start), --base flag (Radix vs Base UI primitives), and `shadcn info` / `shadcn docs` commands for agents. Includes components.json schema, registry-item.json, blocks. Invoke when adding, customising, theming, or scaffolding shadcn components."
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# shadcn/ui —
|
|
7
|
+
# shadcn/ui — CLI v4 + React 19 + Tailwind v4 (2026)
|
|
7
8
|
|
|
8
|
-
**ALWAYS invoke when adding or
|
|
9
|
+
**ALWAYS invoke when adding, customising, theming, or scaffolding shadcn components.**
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
> shadcn/ui is a copy-paste component system, not an npm dependency — components live **in your repo**, get owned and modified there. The CLI keeps them in sync with the upstream registry without hiding the source.
|
|
12
|
+
|
|
13
|
+
## What's new in 2026 (CLI v4 — March 2026)
|
|
14
|
+
|
|
15
|
+
| Change | Impact |
|
|
16
|
+
|---|---|
|
|
17
|
+
| **Tailwind v4 + React 19 first-class** | All components updated; v3+R18 still works (non-breaking) |
|
|
18
|
+
| **`forwardRef` removed** | React 19 treats `ref` as a prop — components are simpler |
|
|
19
|
+
| **`data-slot="…"` on every primitive** | Style any sub-element from outside without overriding source |
|
|
20
|
+
| **HSL → OKLCH** colour tokens | Smoother gradients, perceptual uniformity |
|
|
21
|
+
| **`size-*` utility** | `size-9` instead of `w-9 h-9` |
|
|
22
|
+
| **`default` style deprecated** | Use **`new-york`** (the new default in `components.json`) |
|
|
23
|
+
| **`shadcn/skills`** | AI-agent registry context — agents discover components via the CLI |
|
|
24
|
+
| **`--preset` flag** | Pack colours/theme/fonts/radius into a shareable code string |
|
|
25
|
+
| **`--dry-run` / `--diff` / `--view`** | Inspect registry changes before writing files |
|
|
26
|
+
| **`--template`** | Scaffolds for Next.js, Vite, Laravel, React Router, Astro, TanStack Start |
|
|
27
|
+
| **`--base`** | Choose between **Radix** and **Base UI** primitives |
|
|
28
|
+
| **`shadcn info` / `shadcn docs`** | Context commands for agents and humans |
|
|
29
|
+
|
|
30
|
+
## Initialisation
|
|
11
31
|
|
|
12
32
|
```bash
|
|
13
|
-
|
|
14
|
-
npx shadcn@latest
|
|
33
|
+
# Pick a template (CLI v4 — March 2026)
|
|
34
|
+
npx shadcn@latest init --template next-app
|
|
35
|
+
# Other templates: vite, laravel, react-router, astro, tanstack-start
|
|
36
|
+
|
|
37
|
+
# Add components — non-interactive, idempotent
|
|
38
|
+
npx shadcn@latest add button card dialog input form
|
|
39
|
+
|
|
40
|
+
# Inspect before writing (new in v4)
|
|
41
|
+
npx shadcn@latest add button --dry-run --diff
|
|
42
|
+
|
|
43
|
+
# Use Base UI primitives instead of Radix
|
|
44
|
+
npx shadcn@latest add dialog --base baseui
|
|
15
45
|
```
|
|
16
46
|
|
|
17
|
-
##
|
|
47
|
+
## `components.json` (Tailwind v4 shape)
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
52
|
+
"style": "new-york",
|
|
53
|
+
"rsc": true,
|
|
54
|
+
"tsx": true,
|
|
55
|
+
"tailwind": {
|
|
56
|
+
"config": "",
|
|
57
|
+
"css": "src/app/globals.css",
|
|
58
|
+
"baseColor": "neutral",
|
|
59
|
+
"cssVariables": true,
|
|
60
|
+
"prefix": ""
|
|
61
|
+
},
|
|
62
|
+
"aliases": {
|
|
63
|
+
"components": "@/components",
|
|
64
|
+
"utils": "@/lib/utils",
|
|
65
|
+
"ui": "@/components/ui",
|
|
66
|
+
"lib": "@/lib",
|
|
67
|
+
"hooks": "@/hooks"
|
|
68
|
+
},
|
|
69
|
+
"iconLibrary": "lucide"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> For Tailwind v4 the `tailwind.config` field stays empty — there's no JS config file in v4.
|
|
74
|
+
|
|
75
|
+
## Theming with OKLCH
|
|
18
76
|
|
|
19
77
|
```css
|
|
20
|
-
/* globals.css
|
|
21
|
-
@
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
78
|
+
/* src/app/globals.css */
|
|
79
|
+
@import "tailwindcss";
|
|
80
|
+
|
|
81
|
+
@theme inline {
|
|
82
|
+
/* Light theme — OKLCH (replaces v1 HSL tokens) */
|
|
83
|
+
--color-background: oklch(1.000 0 0);
|
|
84
|
+
--color-foreground: oklch(0.145 0 0);
|
|
85
|
+
|
|
86
|
+
--color-primary: oklch(0.205 0 0);
|
|
87
|
+
--color-primary-foreground: oklch(0.985 0 0);
|
|
88
|
+
|
|
89
|
+
--color-secondary: oklch(0.970 0 0);
|
|
90
|
+
--color-secondary-foreground: oklch(0.205 0 0);
|
|
91
|
+
|
|
92
|
+
--color-muted: oklch(0.970 0 0);
|
|
93
|
+
--color-muted-foreground: oklch(0.556 0 0);
|
|
94
|
+
|
|
95
|
+
--color-destructive: oklch(0.577 0.245 27.325);
|
|
96
|
+
--color-destructive-foreground: oklch(0.985 0 0);
|
|
97
|
+
|
|
98
|
+
--color-border: oklch(0.922 0 0);
|
|
99
|
+
--color-ring: oklch(0.708 0 0);
|
|
100
|
+
|
|
101
|
+
--radius: 0.625rem; /* slightly larger than v1's 0.5rem */
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@theme dark inline {
|
|
105
|
+
--color-background: oklch(0.145 0 0);
|
|
106
|
+
--color-foreground: oklch(0.985 0 0);
|
|
107
|
+
--color-primary: oklch(0.985 0 0);
|
|
108
|
+
--color-primary-foreground: oklch(0.205 0 0);
|
|
109
|
+
/* … rest of dark overrides */
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
OKLCH ranges (rough guide): lightness 0–1, chroma 0–~0.4, hue 0–360°. Pick lightness on the same axis for siblings — gradients stay perceptually smooth.
|
|
114
|
+
|
|
115
|
+
## Components without `forwardRef`
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// React 19 — refs are props, no forwardRef needed
|
|
119
|
+
import * as React from "react";
|
|
120
|
+
import { cn } from "@/lib/utils";
|
|
121
|
+
|
|
122
|
+
function Button({
|
|
123
|
+
ref, // ← just a normal prop
|
|
124
|
+
className,
|
|
125
|
+
variant = "default",
|
|
126
|
+
size = "default",
|
|
127
|
+
...props
|
|
128
|
+
}: React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
129
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
130
|
+
variant?: "default" | "outline" | "ghost" | "destructive";
|
|
131
|
+
size?: "default" | "sm" | "lg" | "icon";
|
|
132
|
+
}) {
|
|
133
|
+
return (
|
|
134
|
+
<button
|
|
135
|
+
ref={ref}
|
|
136
|
+
data-slot="button" // ← every primitive carries data-slot
|
|
137
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
138
|
+
{...props}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
39
141
|
}
|
|
142
|
+
|
|
143
|
+
export { Button };
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`forwardRef` still works — but the new components ship without it.
|
|
147
|
+
|
|
148
|
+
## `data-slot` — style sub-parts from outside
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
// Style the icon inside a Button without touching the Button source
|
|
152
|
+
<Button className="[&_[data-slot=icon]]:size-4 [&_[data-slot=icon]]:text-primary">
|
|
153
|
+
<CheckIcon data-slot="icon" />
|
|
154
|
+
Confirm
|
|
155
|
+
</Button>
|
|
156
|
+
|
|
157
|
+
// Style the trigger of a Dialog from a parent
|
|
158
|
+
<Dialog>
|
|
159
|
+
<DialogTrigger className="[&[data-slot=trigger]]:rounded-full" asChild>
|
|
160
|
+
<Button>Open</Button>
|
|
161
|
+
</DialogTrigger>
|
|
162
|
+
</Dialog>
|
|
40
163
|
```
|
|
41
164
|
|
|
42
|
-
|
|
165
|
+
This replaces the old "expose every internal class through props" pattern — the contract is the slot name, not the className.
|
|
166
|
+
|
|
167
|
+
## Customisation — extend, don't fork
|
|
43
168
|
|
|
44
169
|
```tsx
|
|
45
170
|
// CORRECT — extend via className + variants
|
|
46
|
-
|
|
47
|
-
|
|
171
|
+
<Button variant="outline" size="lg" className="rounded-full">
|
|
172
|
+
Custom
|
|
173
|
+
</Button>
|
|
48
174
|
|
|
49
|
-
//
|
|
175
|
+
// CORRECT — wrap a shadcn primitive when you need a project-specific variant
|
|
176
|
+
function PageHeading({ children, className, ...rest }: ComponentProps<"h1">) {
|
|
177
|
+
return (
|
|
178
|
+
<h1 data-slot="page-heading" className={cn("text-2xl font-semibold tracking-tight", className)} {...rest}>
|
|
179
|
+
{children}
|
|
180
|
+
</h1>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// WRONG — modifying the shadcn source for one-off styles
|
|
185
|
+
// → upgrade path breaks; intent gets lost
|
|
50
186
|
```
|
|
51
187
|
|
|
52
|
-
## Composition
|
|
188
|
+
## Composition
|
|
53
189
|
|
|
54
190
|
```tsx
|
|
55
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from
|
|
56
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
|
|
191
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
192
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
|
57
193
|
|
|
58
|
-
// Compose primitives, don't create monolithic components
|
|
59
194
|
<Dialog>
|
|
60
195
|
<DialogTrigger asChild>
|
|
61
196
|
<Button variant="outline">Open</Button>
|
|
62
197
|
</DialogTrigger>
|
|
63
198
|
<DialogContent>
|
|
64
199
|
<DialogHeader>
|
|
65
|
-
<DialogTitle>
|
|
200
|
+
<DialogTitle>Edit profile</DialogTitle>
|
|
66
201
|
</DialogHeader>
|
|
67
|
-
<Card
|
|
202
|
+
<Card>
|
|
203
|
+
<CardHeader>
|
|
204
|
+
<CardTitle>Account</CardTitle>
|
|
205
|
+
<CardDescription>Update your information.</CardDescription>
|
|
206
|
+
</CardHeader>
|
|
207
|
+
<CardContent>{/* form fields */}</CardContent>
|
|
208
|
+
</Card>
|
|
68
209
|
</DialogContent>
|
|
69
210
|
</Dialog>
|
|
70
211
|
```
|
|
71
212
|
|
|
72
|
-
## Form
|
|
213
|
+
## Forms — shadcn Form + React Hook Form + Zod 4
|
|
73
214
|
|
|
74
215
|
```tsx
|
|
75
|
-
|
|
76
|
-
import {
|
|
77
|
-
import {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
216
|
+
"use client";
|
|
217
|
+
import { z } from "zod";
|
|
218
|
+
import { useForm } from "react-hook-form";
|
|
219
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
220
|
+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
|
221
|
+
import { Input } from "@/components/ui/input";
|
|
222
|
+
import { Button } from "@/components/ui/button";
|
|
223
|
+
|
|
224
|
+
const Schema = z.object({
|
|
225
|
+
email: z.email("Enter a valid email"), // Zod 4 top-level
|
|
226
|
+
password: z.string().min(8, "At least 8 characters"),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
type FormValues = z.infer<typeof Schema>;
|
|
230
|
+
|
|
231
|
+
export function LoginForm() {
|
|
232
|
+
const form = useForm<FormValues>({
|
|
233
|
+
resolver: zodResolver(Schema),
|
|
234
|
+
defaultValues: { email: "", password: "" },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
function onSubmit(values: FormValues) { /* call your API */ }
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<Form {...form}>
|
|
241
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
242
|
+
<FormField control={form.control} name="email" render={({ field }) => (
|
|
243
|
+
<FormItem>
|
|
244
|
+
<FormLabel>Email</FormLabel>
|
|
245
|
+
<FormControl><Input type="email" autoComplete="email" {...field} /></FormControl>
|
|
246
|
+
<FormMessage />
|
|
247
|
+
</FormItem>
|
|
248
|
+
)}/>
|
|
249
|
+
<FormField control={form.control} name="password" render={({ field }) => (
|
|
250
|
+
<FormItem>
|
|
251
|
+
<FormLabel>Password</FormLabel>
|
|
252
|
+
<FormControl><Input type="password" autoComplete="current-password" {...field} /></FormControl>
|
|
253
|
+
<FormMessage />
|
|
254
|
+
</FormItem>
|
|
255
|
+
)}/>
|
|
256
|
+
<Button type="submit" className="w-full">Sign in</Button>
|
|
257
|
+
</form>
|
|
258
|
+
</Form>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Blocks — copy-paste full UI sections
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# List blocks (auth, dashboards, charts, sidebars, calendars, …)
|
|
267
|
+
npx shadcn@latest add --list-blocks
|
|
268
|
+
|
|
269
|
+
# Add a specific block
|
|
270
|
+
npx shadcn@latest add login-04
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Blocks are full sections (login pages, dashboard shells, billing screens) registered as `registry:block` in the catalog. They land in `components/` and you own them after that.
|
|
274
|
+
|
|
275
|
+
## Custom registries
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
// registry-item.json — share components across your team / org
|
|
279
|
+
{
|
|
280
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
281
|
+
"name": "data-table",
|
|
282
|
+
"type": "registry:component",
|
|
283
|
+
"dependencies": ["@tanstack/react-table"],
|
|
284
|
+
"registryDependencies": ["button", "input"],
|
|
285
|
+
"files": [{ "path": "components/data-table.tsx", "type": "registry:component" }]
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Host the JSON anywhere (GitHub Pages, Vercel) and consume:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
npx shadcn@latest add https://your-registry.com/r/data-table.json
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## CLI v4 — workflow commands
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
shadcn info # show project + dependency context (great for AI agents)
|
|
299
|
+
shadcn docs <component> # open docs for a specific component
|
|
300
|
+
shadcn add button --dry-run --view # show what would change, don't write
|
|
301
|
+
shadcn add button --diff # show diff against current files
|
|
302
|
+
shadcn add --preset <hash> # bootstrap an entire design-system from a preset hash
|
|
90
303
|
```
|
|
91
304
|
|
|
92
305
|
## FORBIDDEN
|
|
93
306
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
307
|
+
| ❌ Don't | Why |
|
|
308
|
+
|---|---|
|
|
309
|
+
| Modify component source for one-off styles | Use `className` + `variant` + slots |
|
|
310
|
+
| Skip `init` | The CLI needs `components.json` for paths/aliases |
|
|
311
|
+
| Stay on the deprecated `default` style | Use `new-york` (new CLI default) |
|
|
312
|
+
| Use `forwardRef` in new R19 components | Refs are plain props now |
|
|
313
|
+
| Add `w-10 h-10` instead of `size-10` | Tailwind v4 `size-*` is the recommended utility |
|
|
314
|
+
| HSL tokens for new themes | OKLCH gives smoother gradients & a11y-friendly mixing |
|
|
315
|
+
| Add a component then `npm install` it as a package | shadcn lives **in your repo**, never as a runtime dependency |
|
|
316
|
+
| Break built-in a11y (aria, focus, escape handlers) | Radix/Base UI primitives wire it correctly out of the box |
|
|
317
|
+
|
|
318
|
+
## See Also
|
|
319
|
+
|
|
320
|
+
- `tailwind-patterns` v2 — `@theme`, container queries, OKLCH
|
|
321
|
+
- `react-patterns` v2 — refs as props in R19, `useActionState`, `useOptimistic`
|
|
322
|
+
- `react-standards` v2 — STYLES/LABELS const pattern that pairs with shadcn slots
|
|
323
|
+
- `zod-validation` v2 — Zod 4 + `@hookform/resolvers/zod`
|
|
324
|
+
- `react-ui-patterns` v2 — loading/error/empty states using shadcn primitives
|
|
325
|
+
- `_shared/skills/ui-ux-audit` v2 — WCAG 2.2 AA on shadcn components
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tailwind-patterns
|
|
3
|
-
version:
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "Tailwind CSS v4 (stable Jan 22 2025) CSS-first design system with the Rust-based Oxide engine — 3.78× faster full builds, 8.8× faster incremental, 100× faster on no-CSS-change runs. CSS-first config via `@theme` directive (no `tailwind.config.js`), automatic content detection, native container queries (`@container` + `@md:`), OKLCH color space (perceptual uniformity), modern CSS features (cascade layers, `@property`, `color-mix()`), `@import \"tailwindcss\"` single line. Covers the v3 → v4 migration, semantic-token architecture, mobile-first + container-query responsive strategy, dark mode via `@theme dark`, and the 4px spacing scale. Invoke whenever writing Tailwind classes, theming a project, or designing responsive layouts."
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
#
|
|
7
|
+
# Tailwind CSS v4 — CSS-First Design System (2026)
|
|
7
8
|
|
|
8
9
|
**ALWAYS invoke when writing Tailwind classes, theming, or responsive layouts.**
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## v4 Status (2026)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
- **Tailwind v4.0 stable**: January 22, 2025
|
|
14
|
+
- **Oxide engine** (Rust) — full builds **3.78× faster** (378ms → 100ms), incremental rebuilds **8.8× faster** (44ms → 5ms), no-CSS-change rebuilds **100× faster** (35ms → 192µs)
|
|
15
|
+
- **No `tailwind.config.js`** — config lives in CSS via `@theme`
|
|
16
|
+
- **Automatic content detection** — no `content: [...]` paths to maintain
|
|
17
|
+
- **Native container queries** — no plugin
|
|
18
|
+
- **OKLCH** color space everywhere (perceptual uniformity, smoother gradients)
|
|
19
|
+
- Built-in support for cascade layers, `@property`, `color-mix()`, modern CSS
|
|
20
|
+
|
|
21
|
+
## v3 → v4 cheatsheet
|
|
22
|
+
|
|
23
|
+
| v3 (legacy) | v4 (current) |
|
|
13
24
|
|---|---|
|
|
14
|
-
| `tailwind.config.js` |
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
| Colors in JS
|
|
18
|
-
|
|
|
25
|
+
| `tailwind.config.js` (JS) | `@theme { … }` block (CSS) |
|
|
26
|
+
| `@tailwind base/components/utilities` | `@import "tailwindcss"` (single line) |
|
|
27
|
+
| `content: ["./src/**/*.tsx"]` | Auto-detected (no setup) |
|
|
28
|
+
| Colors in JS object | CSS variables (`--color-primary`) |
|
|
29
|
+
| HSL or hex tokens | **OKLCH** (recommended) |
|
|
30
|
+
| `@tailwindcss/container-queries` plugin | Native (`@container`, `@md:flex-row`) |
|
|
31
|
+
| `dark:` only | `dark:` **and** `@theme dark { … }` for token switching |
|
|
32
|
+
| `@apply` for everything | React components + tokens; reserve `@apply` for legacy CSS |
|
|
33
|
+
|
|
34
|
+
Migration is non-breaking for existing v3 codebases — both can coexist. Run `npx @tailwindcss/upgrade@latest` to automate the bulk.
|
|
19
35
|
|
|
20
36
|
## Setup (v4)
|
|
21
37
|
|
|
@@ -262,17 +278,60 @@ Section gaps: space-y-6 md:space-y-8
|
|
|
262
278
|
Card padding: p-4 md:p-6
|
|
263
279
|
```
|
|
264
280
|
|
|
281
|
+
## Modern CSS — leverage what v4 unlocks
|
|
282
|
+
|
|
283
|
+
```css
|
|
284
|
+
/* color-mix() — derive variants from semantic tokens */
|
|
285
|
+
.btn-primary-soft {
|
|
286
|
+
background: color-mix(in oklch, var(--color-primary) 12%, transparent);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* @property — animatable custom properties */
|
|
290
|
+
@property --gradient-angle {
|
|
291
|
+
syntax: '<angle>';
|
|
292
|
+
inherits: false;
|
|
293
|
+
initial-value: 0deg;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Cascade layers — predictable specificity (Tailwind already uses these) */
|
|
297
|
+
@layer components {
|
|
298
|
+
.card-hero { /* your custom layer */ }
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## `size-*` utility (use over `w-N h-N`)
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
{/* OK — single utility, less repetition */}
|
|
306
|
+
<img className="size-10 rounded-full" />
|
|
307
|
+
<button className="size-9 grid place-items-center">
|
|
308
|
+
|
|
309
|
+
{/* Avoid the duplicate */}
|
|
310
|
+
<img className="w-10 h-10 rounded-full" />
|
|
311
|
+
```
|
|
312
|
+
|
|
265
313
|
## FORBIDDEN
|
|
266
314
|
|
|
267
315
|
| ❌ Don't | ✅ Do |
|
|
268
316
|
|---|---|
|
|
269
317
|
| `tailwind.config.js` in v4 | `@theme` in CSS |
|
|
270
|
-
| `@apply` for everything | React components |
|
|
271
|
-
| `!important` | Fix specificity |
|
|
318
|
+
| `@apply` for everything | React components + token classes |
|
|
319
|
+
| `!important` | Fix specificity (or use cascade layers) |
|
|
272
320
|
| `style={{ color: 'red' }}` | `text-destructive` |
|
|
273
|
-
| Arbitrary values for tokens | Define in `@theme` |
|
|
274
|
-
| `bg-white dark:bg-gray-900` | `bg-background` (semantic) |
|
|
275
|
-
| `@tailwind base` | `@import "tailwindcss"`
|
|
276
|
-
| Dynamic class strings | Static complete strings (
|
|
277
|
-
|
|
|
321
|
+
| Arbitrary values for design tokens (`text-[#2563eb]`) | Define `--color-*` in `@theme` |
|
|
322
|
+
| `bg-white dark:bg-gray-900` | `bg-background` (semantic + `@theme dark`) |
|
|
323
|
+
| `@tailwind base/components/utilities` | `@import "tailwindcss"` |
|
|
324
|
+
| Dynamic class strings (`bg-${color}-500`) | Static complete strings (Oxide can't see derived ones) |
|
|
325
|
+
| HSL tokens for new themes | OKLCH (perceptual uniformity, smoother gradients) |
|
|
326
|
+
| `w-10 h-10` pairs | `size-10` |
|
|
327
|
+
| `forwardRef`-style ref-as-prop indirection | Refs are just props in React 19 |
|
|
328
|
+
| `content: [...]` arrays | Auto-detection in v4 |
|
|
278
329
|
| Inconsistent spacing | Follow 4px scale |
|
|
330
|
+
|
|
331
|
+
## See Also
|
|
332
|
+
|
|
333
|
+
- `react-standards` — STYLES const pattern using these tokens
|
|
334
|
+
- `shadcn-ui` — components built on top of Tailwind v4 + OKLCH + `data-slot`
|
|
335
|
+
- `preline-ui` — alternative token system on top of Tailwind v4
|
|
336
|
+
- `react-ui-patterns` — loading/error/empty states using semantic tokens
|
|
337
|
+
- `_shared/skills/ui-ux-audit` — WCAG 2.2 AA contrast checks against semantic tokens
|