get-shit-pretty 0.7.3 → 0.8.2

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.
Files changed (78) hide show
  1. package/bin/theme-css.js +331 -0
  2. package/gsp/agents/gsp-brand-coherence.md +7 -0
  3. package/gsp/hooks/hooks.json +1 -1
  4. package/gsp/skills/gsp-brand-brief/SKILL.md +50 -5
  5. package/gsp/skills/gsp-brand-guidelines/SKILL.md +51 -31
  6. package/gsp/skills/gsp-brand-guidelines/design-tokens.md +2 -0
  7. package/gsp/skills/gsp-brand-guidelines/guidelines-structure.md +167 -0
  8. package/gsp/skills/gsp-brand-guidelines/methodology/gsp-brand-coherence.md +86 -0
  9. package/gsp/skills/gsp-brand-guidelines/methodology/gsp-brand-engineer.md +13 -5
  10. package/gsp/skills/gsp-brand-guidelines/token-mapping.md +16 -319
  11. package/gsp/skills/gsp-brand-identity/SKILL.md +1 -1
  12. package/gsp/skills/gsp-brand-refine/SKILL.md +5 -6
  13. package/gsp/skills/gsp-brand-research/SKILL.md +1 -1
  14. package/gsp/skills/gsp-brand-strategy/SKILL.md +1 -1
  15. package/gsp/skills/gsp-design-system/SKILL.md +1 -1
  16. package/gsp/skills/gsp-doctor/SKILL.md +51 -3
  17. package/gsp/skills/gsp-progress/SKILL.md +1 -1
  18. package/gsp/skills/gsp-project-brief/SKILL.md +40 -6
  19. package/gsp/skills/gsp-project-build/SKILL.md +27 -31
  20. package/gsp/skills/gsp-project-build/flows/figma.md +50 -0
  21. package/gsp/skills/gsp-project-build/flows/revision.md +64 -0
  22. package/gsp/skills/gsp-project-build/methodology/gsp-project-builder.md +84 -1
  23. package/gsp/skills/gsp-project-build/shadcn-composition.md +217 -0
  24. package/gsp/skills/gsp-project-critique/SKILL.md +3 -1
  25. package/gsp/skills/gsp-project-design/SKILL.md +3 -2
  26. package/gsp/skills/gsp-project-design/methodology/gsp-project-designer.md +28 -21
  27. package/gsp/skills/gsp-project-research/SKILL.md +3 -1
  28. package/gsp/skills/gsp-project-review/SKILL.md +10 -1
  29. package/gsp/skills/gsp-scaffold/SKILL.md +67 -11
  30. package/gsp/skills/gsp-scaffold/shadcn-rules.md +433 -0
  31. package/gsp/skills/gsp-scaffold/shadcn-theming.md +310 -0
  32. package/gsp/skills/gsp-start/SKILL.md +18 -2
  33. package/gsp/skills/gsp-style/SKILL.md +1 -1
  34. package/gsp/skills/gsp-style/style-preset-schema.md +59 -14
  35. package/gsp/skills/gsp-style/styles/academia.yml +58 -8
  36. package/gsp/skills/gsp-style/styles/art-deco.yml +39 -7
  37. package/gsp/skills/gsp-style/styles/bauhaus.yml +39 -8
  38. package/gsp/skills/gsp-style/styles/bold-typography.yml +39 -8
  39. package/gsp/skills/gsp-style/styles/botanical.yml +39 -9
  40. package/gsp/skills/gsp-style/styles/claymorphism.yml +39 -9
  41. package/gsp/skills/gsp-style/styles/cyberpunk.yml +39 -7
  42. package/gsp/skills/gsp-style/styles/enterprise.yml +39 -10
  43. package/gsp/skills/gsp-style/styles/flat-design.yml +39 -9
  44. package/gsp/skills/gsp-style/styles/fluent.yml +39 -10
  45. package/gsp/skills/gsp-style/styles/glassmorphism.yml +39 -8
  46. package/gsp/skills/gsp-style/styles/humanist-literary.yml +39 -9
  47. package/gsp/skills/gsp-style/styles/industrial.yml +59 -9
  48. package/gsp/skills/gsp-style/styles/kinetic.yml +32 -7
  49. package/gsp/skills/gsp-style/styles/liquid-glass.yml +59 -9
  50. package/gsp/skills/gsp-style/styles/luxury.yml +59 -9
  51. package/gsp/skills/gsp-style/styles/material.yml +59 -9
  52. package/gsp/skills/gsp-style/styles/maximalism.yml +32 -6
  53. package/gsp/skills/gsp-style/styles/minimal-dark.yml +32 -7
  54. package/gsp/skills/gsp-style/styles/modern-dark.yml +32 -7
  55. package/gsp/skills/gsp-style/styles/monochrome.yml +59 -10
  56. package/gsp/skills/gsp-style/styles/neubrutalism.yml +59 -9
  57. package/gsp/skills/gsp-style/styles/neumorphism.yml +32 -7
  58. package/gsp/skills/gsp-style/styles/newsprint.yml +32 -7
  59. package/gsp/skills/gsp-style/styles/nothing.yml +44 -13
  60. package/gsp/skills/gsp-style/styles/organic.yml +42 -9
  61. package/gsp/skills/gsp-style/styles/playful-geometric.yml +43 -9
  62. package/gsp/skills/gsp-style/styles/professional.yml +41 -10
  63. package/gsp/skills/gsp-style/styles/retro.yml +42 -8
  64. package/gsp/skills/gsp-style/styles/saas.yml +42 -9
  65. package/gsp/skills/gsp-style/styles/sketch.yml +42 -9
  66. package/gsp/skills/gsp-style/styles/swiss-minimalist.yml +41 -10
  67. package/gsp/skills/gsp-style/styles/terminal.yml +42 -8
  68. package/gsp/skills/gsp-style/styles/vaporwave.yml +42 -7
  69. package/gsp/skills/gsp-style/styles/web3.yml +42 -9
  70. package/gsp/templates/branding/brief.md +9 -0
  71. package/gsp/templates/branding/config.json +1 -1
  72. package/gsp/templates/design-claude.md +6 -0
  73. package/gsp/templates/phases/design.md +0 -8
  74. package/gsp/templates/phases/patterns.md +2 -2
  75. package/gsp/templates/projects/config.json +6 -3
  76. package/gsp/templates/projects/state.md +1 -1
  77. package/gsp/templates/system/STACK.md +1 -0
  78. package/package.json +1 -1
@@ -0,0 +1,433 @@
1
+ # shadcn/ui Rules — Tier 1: Install & Config
2
+
3
+ Rules for the scaffold phase. The foundations agent reads this to set up a correct shadcn/ui installation.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npx shadcn@latest init -d
11
+ ```
12
+
13
+ The `-d` flag uses defaults — it forces **Next.js + nova preset**. For other frameworks (Vite, React Router, Remix) or to pick a different preset, omit `-d` and answer the prompts, or use the `-t` / `-p` flags:
14
+
15
+ ```bash
16
+ npx shadcn@latest init -t vite # scaffold for Vite
17
+ npx shadcn@latest init -t next -p lyra # Next.js with lyra preset
18
+ npx shadcn@latest init --monorepo # monorepo scaffold (prompts for framework)
19
+ npx shadcn@latest init -t next --monorepo # Next.js monorepo
20
+ npx shadcn@latest init # interactive — choose framework + style
21
+ ```
22
+
23
+ Templates: `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`), `laravel` (no monorepo).
24
+
25
+ After init, capture project context:
26
+
27
+ ```bash
28
+ npx shadcn@latest info --json
29
+ ```
30
+
31
+ Key fields from the JSON:
32
+
33
+ | Field | Use |
34
+ |-------|-----|
35
+ | `aliases` | Import prefix (`@/`, `~/`) — use consistently in all generated code |
36
+ | `tailwindVersion` | `"v4"` uses `@theme inline`; `"v3"` uses `tailwind.config.js` |
37
+ | `tailwindCssFile` | Where CSS custom properties go — write tokens here |
38
+ | `style` | Component visual treatment (`new-york`, `base-nova`, etc.) — do NOT assume `default` (deprecated) |
39
+ | `iconLibrary` | `lucide-react` or `@tabler/icons-react` — use the right import |
40
+ | `resolvedPaths` | Exact destinations for components, utils, hooks |
41
+ | `isRSC` | Whether `"use client"` directives are needed |
42
+
43
+ ---
44
+
45
+ ## globals.css pattern
46
+
47
+ ### Tailwind v4
48
+
49
+ ```css
50
+ @import "tailwindcss";
51
+ @import "shadcn/tailwind.css";
52
+
53
+ @custom-variant dark (&:is(.dark *));
54
+
55
+ @theme inline {
56
+ --color-background: var(--background);
57
+ --color-foreground: var(--foreground);
58
+ --color-card: var(--card);
59
+ --color-card-foreground: var(--card-foreground);
60
+ --color-popover: var(--popover);
61
+ --color-popover-foreground: var(--popover-foreground);
62
+ --color-primary: var(--primary);
63
+ --color-primary-foreground: var(--primary-foreground);
64
+ --color-secondary: var(--secondary);
65
+ --color-secondary-foreground: var(--secondary-foreground);
66
+ --color-muted: var(--muted);
67
+ --color-muted-foreground: var(--muted-foreground);
68
+ --color-accent: var(--accent);
69
+ --color-accent-foreground: var(--accent-foreground);
70
+ --color-destructive: var(--destructive);
71
+ --color-border: var(--border);
72
+ --color-input: var(--input);
73
+ --color-ring: var(--ring);
74
+ --color-chart-1: var(--chart-1);
75
+ --color-chart-2: var(--chart-2);
76
+ --color-chart-3: var(--chart-3);
77
+ --color-chart-4: var(--chart-4);
78
+ --color-chart-5: var(--chart-5);
79
+ --color-sidebar: var(--sidebar);
80
+ --color-sidebar-foreground: var(--sidebar-foreground);
81
+ --color-sidebar-primary: var(--sidebar-primary);
82
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
83
+ --color-sidebar-accent: var(--sidebar-accent);
84
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
85
+ --color-sidebar-border: var(--sidebar-border);
86
+ --color-sidebar-ring: var(--sidebar-ring);
87
+ --radius-sm: calc(var(--radius) * 0.6);
88
+ --radius-md: calc(var(--radius) * 0.8);
89
+ --radius-lg: var(--radius);
90
+ --radius-xl: calc(var(--radius) * 1.4);
91
+ --radius-2xl: calc(var(--radius) * 1.8);
92
+ --radius-3xl: calc(var(--radius) * 2.2);
93
+ --radius-4xl: calc(var(--radius) * 2.6);
94
+ }
95
+
96
+ :root {
97
+ /* Insert OKLCH custom properties from bin/theme-css.js output here */
98
+ }
99
+
100
+ .dark {
101
+ /* Insert .dark overrides from bin/theme-css.js output here */
102
+ }
103
+
104
+ @layer base {
105
+ * {
106
+ @apply border-border outline-ring/50;
107
+ }
108
+ body {
109
+ @apply bg-background text-foreground;
110
+ }
111
+ button:not(:disabled), [role="button"]:not(:disabled) {
112
+ cursor: pointer;
113
+ }
114
+ }
115
+ ```
116
+
117
+ **Note:** `shadcn/tailwind.css` ships with the shadcn CLI — no separate npm install needed. Do **not** use `tw-animate-css`; that's a different package with different animation primitives.
118
+
119
+ ### Google Fonts (optional)
120
+
121
+ If the preset uses a Google Font (check `typography.font-family-primary`), add the import **before** the Tailwind import:
122
+
123
+ ```css
124
+ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
125
+ @import "tailwindcss";
126
+ @import "shadcn/tailwind.css";
127
+ ```
128
+
129
+ Then set font vars with literal values in `@theme inline` (not `var()` self-references):
130
+
131
+ ```css
132
+ @theme inline {
133
+ /* … existing color/radius vars … */
134
+ --font-sans: "Inter", sans-serif;
135
+ --font-mono: "JetBrains Mono", monospace;
136
+ }
137
+ ```
138
+
139
+ `bin/theme-css.js` emits `--font-sans` / `--font-mono` / `--font-display` from the preset's `typography` block — paste those values (not the var declarations) into `@theme inline`.
140
+
141
+ ### Tailwind v3
142
+
143
+ ```css
144
+ @tailwind base;
145
+ @tailwind components;
146
+ @tailwind utilities;
147
+
148
+ @layer base {
149
+ :root {
150
+ /* Insert HSL custom properties here */
151
+ }
152
+ .dark {
153
+ /* Insert .dark overrides here */
154
+ }
155
+ }
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Token injection
161
+
162
+ **Order matters** — run component installs first, then overwrite tokens. This prevents `shadcn add` from appending its own `cssVars` after your custom OKLCH values.
163
+
164
+ **Step 1 — Install all components first:**
165
+
166
+ ```bash
167
+ npx shadcn@latest add button card dialog popover select tooltip ...
168
+ ```
169
+
170
+ **Step 2 — Generate and inject brand tokens:**
171
+
172
+ ```bash
173
+ node bin/theme-css.js .design/branding/{brand}/patterns/{brand}.yml --stdout
174
+ ```
175
+
176
+ Paste the `:root { }` and `.dark { }` output into `globals.css`, replacing any `:root`/`.dark` blocks the shadcn CLI wrote. The `@theme inline` block stays untouched — it only contains `var()` aliases and radius/font values, not actual color values.
177
+
178
+ **Format:** OKLCH (`oklch(L C H)`). shadcn/ui v2+ accepts OKLCH natively. No `hsl()` wrapper needed.
179
+
180
+ ---
181
+
182
+ ## Project context
183
+
184
+ Always run this first — it tells you exactly what the project has:
185
+
186
+ ```bash
187
+ npx shadcn@latest info --json
188
+ ```
189
+
190
+ Key fields to check before writing any code:
191
+
192
+ | Field | Use |
193
+ |-------|-----|
194
+ | `base` | `"radix"` or `"base"` — determines component APIs (asChild vs render, ToggleGroup type prop, Slider value shape, etc.) |
195
+ | `tailwindVersion` | `"v4"` uses `@theme inline`; `"v3"` uses `tailwind.config.js` |
196
+ | `tailwindCssFile` | The exact CSS file to edit — never create a new one |
197
+ | `iconLibrary` | `lucide-react` or `@tabler/icons-react` — never assume lucide |
198
+ | `aliases` | Import prefix — use consistently, never relative paths to UI files |
199
+ | `isRSC` | When `true`, components with hooks/event handlers need `"use client"` |
200
+ | `packageManager` | Use for non-shadcn dep installs (`pnpm add date-fns` vs `npm install date-fns`) |
201
+
202
+ ---
203
+
204
+ ## Presets
205
+
206
+ Three formats for `--preset`:
207
+
208
+ ```bash
209
+ npx shadcn@latest init --preset nova # named preset
210
+ npx shadcn@latest init --preset a2r6bw # opaque code from ui.shadcn.com
211
+ npx shadcn@latest init --preset "https://..." # full URL
212
+ ```
213
+
214
+ **Never decode or fetch preset codes manually.** They are opaque — pass them directly to the CLI.
215
+
216
+ ### Applying a preset to an existing project
217
+
218
+ ```bash
219
+ npx shadcn@latest apply --preset a2r6bw # overwrite all detected component files
220
+ npx shadcn@latest apply a2r6bw # shorthand
221
+ ```
222
+
223
+ ### Switching presets
224
+
225
+ Ask the user which strategy before running:
226
+
227
+ | Strategy | Command | When |
228
+ |----------|---------|------|
229
+ | **Overwrite** | `npx shadcn@latest apply --preset <code>` | User hasn't customized components |
230
+ | **Merge** | `npx shadcn@latest init --preset <code> --force --no-reinstall`, then smart merge per component | User has customized components |
231
+ | **Skip** | `npx shadcn@latest init --preset <code> --force --no-reinstall` | Update config/CSS only, leave components |
232
+
233
+ Named presets: `nova`, `vega`, `maia`, `lyra`, `mira`, `luma`
234
+
235
+ ---
236
+
237
+ ## Component install
238
+
239
+ ```bash
240
+ # Install individual components
241
+ npx shadcn@latest add button card input
242
+
243
+ # Install all at once (preferred — from install-manifest.md)
244
+ npx shadcn@latest add button card dialog popover select tooltip ...
245
+
246
+ # Preview all files that would be written (no changes)
247
+ npx shadcn@latest add button --dry-run
248
+
249
+ # Preview diff for a specific file before updating
250
+ npx shadcn@latest add button --diff button.tsx
251
+
252
+ # Preview CSS changes
253
+ npx shadcn@latest add button --diff globals.css
254
+
255
+ # View component source without installing
256
+ npx shadcn@latest add button --view button.tsx
257
+
258
+ # Overwrite existing files
259
+ npx shadcn@latest add button --overwrite
260
+
261
+ # Install from a community registry
262
+ npx shadcn@latest add @shadcnblocks/sidebar-07
263
+ ```
264
+
265
+ **Always use `--dry-run` or `--diff` before overwriting existing components.** Never fetch raw component source from GitHub — the CLI handles registry resolution, paths, and CSS diffing.
266
+
267
+ ### Get component docs before building or fixing
268
+
269
+ ```bash
270
+ npx shadcn@latest docs button dialog select
271
+ ```
272
+
273
+ Returns live documentation and example URLs. Fetch those URLs to get the actual API and usage patterns. Run this before implementing or debugging any component.
274
+
275
+ ### Before building a custom component
276
+
277
+ Run `npx shadcn@latest search {name}` — a registry version may already exist:
278
+
279
+ ```bash
280
+ npx shadcn@latest search sidebar
281
+ npx shadcn@latest search @tailark -q "stats"
282
+ ```
283
+
284
+ ### Post-install import check (community registries)
285
+
286
+ After installing from a community registry (e.g. `@bundui`, `@magicui`, `@shadcnblocks`), check added non-UI files for hardcoded import paths like `@/components/ui/...`. These may not match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias and rewrite imports accordingly. The CLI rewrites its own UI files — third-party files may use default paths.
287
+
288
+ ### Registry gate
289
+
290
+ When the user asks to add a block or component without specifying a registry, ask which registry to use. Never default to one silently. When the user specifies a registry, always use exactly that one.
291
+
292
+ **If a batch install fails:** parse the error, remove the failing component(s), retry with the rest. Log failures.
293
+
294
+ **`registryDependencies` are installed automatically** — complex components pull their own deps in one step.
295
+
296
+ Known registry gaps:
297
+ - `visually-hidden` — implement as a simple CSS utility instead
298
+
299
+ ---
300
+
301
+ ## Blocks
302
+
303
+ Blocks are pre-built page sections — install them the same way as components:
304
+
305
+ ```bash
306
+ npx shadcn@latest add sidebar-07 # install a specific block
307
+ npx shadcn@latest add sidebar-07 --diff # preview changes before installing
308
+ npx shadcn@latest add sidebar-07 --overwrite # force overwrite existing files
309
+ ```
310
+
311
+ **Always check for a block before building from scratch.** Run `npx shadcn@latest search {name}` first.
312
+
313
+ ### Block categories
314
+
315
+ | Category | Slugs | Page target |
316
+ |----------|-------|-------------|
317
+ | Sidebar | `sidebar-01` … `sidebar-16` | `app/dashboard/page.tsx` |
318
+ | Login | `login-01` … `login-05` | `app/login/page.tsx` |
319
+ | Signup | `signup-01` … `signup-05` | `app/signup/page.tsx` |
320
+
321
+ Each block installs two things: a **page.tsx** at the fixed route above, and one or more component files. The page path is fixed — the agent cannot change it without manual refactoring.
322
+
323
+ ### Sidebar blocks (01–16)
324
+
325
+ All 16 sidebar blocks share the same structural shell:
326
+
327
+ ```tsx
328
+ <SidebarProvider style={{ "--sidebar-width": "19rem" } as React.CSSProperties}>
329
+ <AppSidebar />
330
+ <SidebarInset>
331
+ <header>
332
+ <SidebarTrigger />
333
+ <Breadcrumb />
334
+ </header>
335
+ {/* page content */}
336
+ </SidebarInset>
337
+ </SidebarProvider>
338
+ ```
339
+
340
+ Key props:
341
+ - `collapsible` — `"icon"` | `"offcanvas"` | `"none"`
342
+ - `variant` — default | `"inset"` | `"floating"`
343
+ - `useSidebar()` hook — exposes `isMobile` for responsive dropdown positioning
344
+ - `--sidebar-width` goes on `<SidebarProvider>` as an inline style, **not** in global CSS
345
+
346
+ `registryDependencies` (sidebar, breadcrumb, card, etc.) install automatically with the block.
347
+
348
+ ### Login blocks (01–05)
349
+
350
+ UI-only. What agents must add manually:
351
+ - Form handling (`react-hook-form` + `zod` + server actions)
352
+ - OAuth provider wiring — buttons render as plain `<Button variant="outline">` shells
353
+ - Error states and redirect logic
354
+
355
+ The blocks use the `field` primitive (`<Field>`, `<FieldLabel>`, `<FieldGroup>`, `<FieldDescription>`) — **not** a plain `<label>`. See the Form Field API section in builder rules.
356
+
357
+ ### Signup blocks (01–05)
358
+
359
+ Parallel structure to login blocks — same UI-only caveat. Add form handling and validation.
360
+
361
+ ### Community registries
362
+
363
+ ```bash
364
+ # Search community registries
365
+ npx shadcn@latest search dashboard
366
+
367
+ # Install from a community registry
368
+ npx shadcn@latest add @shadcnblocks/dashboard
369
+
370
+ # Preview registry item before installing
371
+ npx shadcn@latest add @shadcnblocks/dashboard --diff
372
+ ```
373
+
374
+ 148 community registries are available via `@namespace/item` syntax. Always check with `--diff` before installing community items — they may conflict with existing components.
375
+
376
+ ---
377
+
378
+ ## base vs radix
379
+
380
+ Check `base` from `npx shadcn@latest info` before writing any component code. The two primitives have different APIs:
381
+
382
+ | Area | radix | base |
383
+ |------|-------|------|
384
+ | Custom trigger | `asChild` prop | `render` prop |
385
+ | Button as link | `<Button asChild><a>` | `<Button render={<a />} nativeButton={false}>` |
386
+ | Select items | Inline JSX only | Requires `items` prop on root + JSX |
387
+ | Select placeholder | `<SelectValue placeholder="…">` | `{ value: null }` item in items array |
388
+ | ToggleGroup single | `type="single"` + string `defaultValue` | No `type` prop + array `defaultValue` |
389
+ | ToggleGroup multi | `type="multiple"` | `multiple` boolean prop |
390
+ | Slider | Array always (`defaultValue={[50]}`) | Plain number for single thumb (`defaultValue={50}`) |
391
+ | Accordion | `type="single"/"multiple"` + `collapsible` + string `defaultValue` | `multiple` boolean + array `defaultValue` |
392
+
393
+ **Trigger composition:**
394
+
395
+ ```tsx
396
+ // radix
397
+ <DialogTrigger asChild>
398
+ <Button>Open</Button>
399
+ </DialogTrigger>
400
+
401
+ // base
402
+ <DialogTrigger render={<Button />}>Open</DialogTrigger>
403
+ ```
404
+
405
+ For full API differences with code examples, read `${CLAUDE_SKILL_DIR}/../../gsp-scaffold/shadcn-theming.md` or run `npx shadcn@latest docs <component>`.
406
+
407
+ ---
408
+
409
+ ## Critical rules
410
+
411
+ 1. **Never overwrite components.json** — shadcn init writes it; subsequent adds read it. The `style`, `tailwind.baseColor`, and `tailwind.cssVariables` fields are immutable after init — changing them requires deleting all installed components and reinstalling
412
+ 2. **Check for existing files before overwriting** — `globals.css` may already have Tailwind imports
413
+ 3. **Tailwind v4 source scoping** — if the repo has non-source files with CSS class names (`.design/`, `gsp/`), add `source("../")` to the `@import "tailwindcss"` line to limit scanning
414
+ 4. **Import alias consistency** — all generated code must use the alias from `shadcn info` (`@/` or `~/`), never relative paths to component files
415
+ 5. **Never hardcode colors** — use `bg-primary`, `text-muted-foreground`, `border-border`, etc. — never `bg-blue-500`
416
+ 6. **Install components before writing tokens** — run `shadcn add` first; then overwrite `:root`/`.dark` with `bin/theme-css.js` output. Reversing this order risks shadcn appending its own vars after yours
417
+
418
+ ---
419
+
420
+ ## Dark mode setup
421
+
422
+ shadcn uses class-based dark mode. In `tailwind.config.js` (v3):
423
+
424
+ ```js
425
+ module.exports = {
426
+ darkMode: ["class"],
427
+ // ...
428
+ }
429
+ ```
430
+
431
+ In v4 with `@custom-variant dark` — no config needed; the variant is declared in CSS.
432
+
433
+ The root layout needs a `ThemeProvider` if the project requires user-togglable dark mode. For dark-by-default or light-only, skip the provider and set the class on `<html>` directly.