@minhduydev/mdpi 0.3.2 → 0.4.1

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 (36) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +202 -4
  3. package/dist/template/.pi/README.md +29 -8
  4. package/dist/template/.pi/VERSION +1 -1
  5. package/dist/template/.pi/agents/general.md +1 -1
  6. package/dist/template/.pi/agents/review.md +1 -1
  7. package/dist/template/.pi/extensions/templates-injector.ts +1 -1
  8. package/dist/template/.pi/packages.json +13 -0
  9. package/dist/template/.pi/prompts/audit.md +4 -1
  10. package/dist/template/.pi/prompts/create.md +1 -0
  11. package/dist/template/.pi/prompts/fix.md +7 -1
  12. package/dist/template/.pi/prompts/gc.md +3 -0
  13. package/dist/template/.pi/prompts/init.md +3 -0
  14. package/dist/template/.pi/prompts/plan.md +5 -2
  15. package/dist/template/.pi/prompts/research.md +3 -0
  16. package/dist/template/.pi/prompts/ship.md +7 -1
  17. package/dist/template/.pi/prompts/verify.md +4 -1
  18. package/dist/template/.pi/skills/INDEX.md +49 -12
  19. package/dist/template/.pi/skills/accessibility-audit/SKILL.md +8 -2
  20. package/dist/template/.pi/skills/baseline-ui/SKILL.md +211 -0
  21. package/dist/template/.pi/skills/dcp-hygiene/SKILL.md +106 -0
  22. package/dist/template/.pi/skills/design-taste-frontend/SKILL.md +53 -42
  23. package/dist/template/.pi/skills/fixing-accessibility/SKILL.md +509 -0
  24. package/dist/template/.pi/skills/frontend-design/SKILL.md +59 -46
  25. package/dist/template/.pi/skills/frontend-ui-engineering/SKILL.md +21 -27
  26. package/dist/template/.pi/skills/memory-system/SKILL.md +118 -0
  27. package/dist/template/.pi/skills/oklch-color-workflow/SKILL.md +426 -0
  28. package/dist/template/.pi/skills/production-hardening/SKILL.md +652 -0
  29. package/dist/template/.pi/skills/ui-craft-principles/SKILL.md +564 -0
  30. package/dist/template/.pi/skills/ui-quality-audit/SKILL.md +329 -0
  31. package/dist/template/.pi/templates/DESIGN.md +76 -0
  32. package/dist/template/.pi/workflows/INDEX.md +2 -1
  33. package/dist/template/.pi/workflows/frontend-feature-workflow.md +343 -0
  34. package/dist/template/.pi/workflows/quality-loop.md +3 -1
  35. package/package.json +1 -1
  36. /package/dist/template/.pi/templates/{design.md → feature-design.md} +0 -0
@@ -0,0 +1,426 @@
1
+ ---
2
+ name: oklch-color-workflow
3
+ description: Complete OKLCH color system workflow — syntax, thresholds, conversion, palette generation, contrast checking, gamut mapping, and Tailwind v4 migration
4
+ ---
5
+
6
+ # OKLCH Color Workflow
7
+
8
+ ## When to Use
9
+
10
+ - When defining design tokens or a color palette for a new project
11
+ - When migrating from HEX/RGB to OKLCH in Tailwind v4
12
+ - When needing wider-gamut colors for modern displays (P3, Rec.2020)
13
+ - When creating accessible color systems with predictable lightness and contrast
14
+ - When building design systems that need scientific, perceptually-uniform color
15
+
16
+ ## When NOT to Use
17
+
18
+ - Projects not using CSS or design tokens (native mobile apps without color system migration)
19
+ - When a pre-built palette (Tailwind default colors, shadcn/ui defaults) is sufficient
20
+ - Quick prototypes where color system design is premature
21
+
22
+ ---
23
+
24
+ ## 1. OKLCH Syntax
25
+
26
+ OKLCH is a perceptually-uniform color space: equal changes in values produce equal changes in perceived color.
27
+
28
+ ```css
29
+ /* Syntax: oklch(L C H / alpha) */
30
+ /* L: Lightness — 0 (black) to 1 (white) */
31
+ /* C: Chroma — 0 (gray) to ~0.37 (maximum) */
32
+ /* H: Hue — 0 to 360 degrees */
33
+ /* alpha: Optional — 0 to 1 */
34
+
35
+ oklch(0.5 0.2 280) /* Medium purple-blue */
36
+ oklch(0.9 0.02 0) /* Near-white (very low chroma) */
37
+ oklch(0.4 0.25 160 / 0.8) /* Green with opacity */
38
+ oklch(0.5 0 0) /* Perfect neutral gray — C=0 means no hue */
39
+ ```
40
+
41
+ **Key insight:** Unlike HSL where `hsl(0 0% 50%)` is medium gray, OKLCH gray is `oklch(0.5 0 0)` — and it's perceptually the same lightness regardless of hue. HSL's perceived lightness varies by hue (yellows look lighter than blues at the same HSL lightness).
42
+
43
+ ### CSS OKLCH vs HSL comparison:
44
+
45
+ | Property | HSL | OKLCH |
46
+ |----------|-----|-------|
47
+ | Perceptual uniformity | No — lightness is hue-dependent | Yes — same L = same perceived lightness |
48
+ | Gray point | `hsl(0 0% X%)` — abstract | `oklch(L 0 H)` — C=0 at any hue = gray |
49
+ | Gamut | Always sRGB | Extends to P3, Rec.2020 |
50
+ | Browser support | Universal | 93%+ (Chrome 111+, Safari 15.4+, Firefox 113+) |
51
+
52
+ ---
53
+
54
+ ## 2. Threshold Values
55
+
56
+ | Range | Chroma (C) | Use |
57
+ |-------|-----------|-----|
58
+ | **Gray / Neutral** | `0` — `0.02` | Backgrounds, text, borders, dividers |
59
+ | **Subtle** | `0.02` — `0.08` | Tinted neutrals, subtle surfaces, muted text |
60
+ | **Accent** | `0.15` — `0.25` | Buttons, links, highlights, brand colors |
61
+ | **Vibrant** | `0.25` — `0.37` | Vibrant accents, marketing elements, illustrations |
62
+
63
+ **Lightness thresholds for text:**
64
+
65
+ | Use | L (Lightness) | Notes |
66
+ |-----|--------------|-------|
67
+ | Dark text on light bg | `0.15` — `0.4` | Below 0.15 is too close to black |
68
+ | Light text on dark bg | `0.7` — `0.95` | Above 0.95 is too close to white |
69
+ | Body text | `0.2` — `0.35` | Comfortable reading range |
70
+ | Muted/secondary | `0.4` — `0.6` | Lower contrast text |
71
+ | Surface backgrounds (light) | `0.95` — `1` | Near-white |
72
+ | Surface backgrounds (dark) | `0.1` — `0.2` | Near-black |
73
+
74
+ **Hue ranges for common colors:**
75
+
76
+ | Color | Hue (H) |
77
+ |-------|---------|
78
+ | Red | 20–40 |
79
+ | Orange | 50–75 |
80
+ | Yellow | 85–110 |
81
+ | Green | 130–160 |
82
+ | Teal | 170–200 |
83
+ | Blue | 220–270 |
84
+ | Purple | 280–310 |
85
+ | Pink | 320–350 |
86
+ | Neutral | 0 (any, C should be 0) |
87
+
88
+ ---
89
+
90
+ ## 3. Conversion
91
+
92
+ ### HEX/RGB → OKLCH
93
+
94
+ Use `color-mix()` in CSS or a conversion function:
95
+
96
+ ```ts
97
+ // JavaScript conversion using the CSS Color API
98
+ function hexToOklch(hex: string): { L: number; C: number; H: number } {
99
+ const canvas = new OffscreenCanvas(1, 1);
100
+ const ctx = canvas.getContext('2d')!;
101
+ ctx.fillStyle = hex;
102
+ ctx.fillRect(0, 0, 1, 1);
103
+ const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
104
+
105
+ // Convert sRGB linear → OKLab → OKLCH
106
+ // Use a library instead of manual math for production
107
+ return srgbToOklch([r / 255, g / 255, b / 255]);
108
+ }
109
+
110
+ // Recommended: use `culori` or `colorjs.io` library
111
+ import { rgb, oklch } from 'culori';
112
+
113
+ const color = oklch(rgb('#3B82F6'));
114
+ // → { L: 0.54, C: 0.18, H: 260, alpha: 1 }
115
+ ```
116
+
117
+ **Online tools:**
118
+ - [oklch.com](https://oklch.com) — interactive OKLCH picker
119
+ - [huetone.art](https://huetone.art) — palette generation in OKLCH
120
+ - [colourcontrast.cc](https://colourcontrast.cc) — contrast checking with OKLCH
121
+
122
+ ### OKLCH → HEX (with gamut mapping)
123
+
124
+ ```css
125
+ /* Direct — browser handles gamut mapping automatically */
126
+ color: oklch(0.5 0.3 280);
127
+
128
+ /* Fallback for older browsers */
129
+ color: #6b5ae8;
130
+ color: oklch(0.5 0.3 280);
131
+ ```
132
+
133
+ ```ts
134
+ import { oklch, rgb } from 'culori';
135
+
136
+ // Convert OKLCH to sRGB hex (gamut mapped automatically)
137
+ const hex = rgb(oklch('oklch(0.5 0.3 280)'));
138
+ // Or with explicit gamut mapping to sRGB:
139
+ import { clampChroma, toGamut } from 'culori';
140
+
141
+ const gamutMapped = toGamut('srgb', 'oklch')(oklch('oklch(0.5 0.3 280)'));
142
+ ```
143
+
144
+ ---
145
+
146
+ ## 4. Palette Generation
147
+
148
+ ### Single-hue palette (50–950 scale)
149
+
150
+ Given a hue and target lightnesses, vary L and C to create a consistent palette:
151
+
152
+ ```css
153
+ /* Blue palette — single hue, varying lightness + chroma */
154
+ :root {
155
+ --blue-50: oklch(0.97 0.01 260);
156
+ --blue-100: oklch(0.93 0.03 260);
157
+ --blue-200: oklch(0.86 0.06 260);
158
+ --blue-300: oklch(0.78 0.10 260);
159
+ --blue-400: oklch(0.68 0.15 260);
160
+ --blue-500: oklch(0.56 0.18 260);
161
+ --blue-600: oklch(0.45 0.18 260);
162
+ --blue-700: oklch(0.35 0.15 260);
163
+ --blue-800: oklch(0.27 0.12 260);
164
+ --blue-900: oklch(0.20 0.08 260);
165
+ --blue-950: oklch(0.14 0.04 260);
166
+ }
167
+ ```
168
+
169
+ **Chroma curve for natural palettes:**
170
+ - 50: C=0.01 (near gray)
171
+ - Peak C at 500–600 (the most "colorful")
172
+ - 950: C=0.02–0.05 (near gray again)
173
+
174
+ ### Semantic tokens from primitives
175
+
176
+ ```css
177
+ :root {
178
+ /* Background */
179
+ --bg-primary: oklch(0.99 0 0);
180
+ --bg-secondary: oklch(0.96 0.02 260);
181
+ --bg-muted: oklch(0.94 0.01 260);
182
+
183
+ /* Foreground */
184
+ --fg-primary: oklch(0.15 0 0);
185
+ --fg-muted: oklch(0.45 0.01 260);
186
+
187
+ /* Brand */
188
+ --color-primary: oklch(0.56 0.18 260);
189
+ --color-accent: oklch(0.62 0.22 180);
190
+
191
+ /* Borders */
192
+ --border-default: oklch(0.88 0.01 260);
193
+ --border-muted: oklch(0.92 0.01 260);
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 5. Contrast (APCA / WCAG 3)
200
+
201
+ OKLCH makes contrast calculation perceptually accurate. Use the lightness difference (`ΔL`) for quick estimates.
202
+
203
+ ```css
204
+ /* Quick contrast heuristic in OKLCH: */
205
+ /* WCAG 2.1 AA (4.5:1) ≈ |L₁ - L₂| ≥ 0.35 */
206
+ /* WCAG 2.1 AA Large (3:1) ≈ |L₁ - L₂| ≥ 0.25 */
207
+ /* WCAG 2.1 AAA (7:1) ≈ |L₁ - L₂| ≥ 0.50 */
208
+
209
+ /* Example: white text on blue button */
210
+ /* White: L=1, Blue-500: L=0.56, ΔL=0.44 → ≥ 0.35 ✓ AA pass */
211
+ button {
212
+ background: oklch(0.56 0.18 260); /* Blue-500 */
213
+ color: oklch(1 0 0); /* White */
214
+ }
215
+
216
+ /* Example: gray text on white background — FAIL */
217
+ /* Gray-400: L=0.63, White: L=1, ΔL=0.37 → barely passes AA */
218
+ .text-muted {
219
+ color: oklch(0.63 0.01 260); /* Gray-400 */
220
+ }
221
+
222
+ /* Better: */
223
+ .text-muted {
224
+ color: oklch(0.45 0.01 260); /* Gray-600 — ΔL=0.55 ✓ */
225
+ }
226
+ ```
227
+
228
+ **Contrast check recipe:**
229
+
230
+ ```ts
231
+ function contrastRatio(L1: number, L2: number): number {
232
+ // OKLCH-based APCA-like contrast
233
+ const lighter = Math.max(L1, L2);
234
+ const darker = Math.min(L1, L2);
235
+ const contrast = (lighter + 0.1) / (darker + 0.1);
236
+ return contrast;
237
+ }
238
+
239
+ // Use with culori for precise WCAG 3 APCA:
240
+ import { contrastAPCA } from 'culori';
241
+
242
+ const ratio = contrastAPCA('oklch(0.15 0 0)', 'oklch(0.97 0 0)');
243
+ // APCA values: 0-100+, 45+ = preferred for body text
244
+ ```
245
+
246
+ ---
247
+
248
+ ## 6. Gamut Mapping
249
+
250
+ OKLCH colors can fall outside the sRGB gamut. Browsers automatically clip them, but you can control the mapping:
251
+
252
+ ```css
253
+ /* This color is outside sRGB — vivid saturated blue-purple */
254
+ .vibrant-element {
255
+ color: oklch(0.5 0.37 290);
256
+ /* Browser clips to nearest sRGB color automatically */
257
+ /* Result will be approximately #8855ff */
258
+ }
259
+ ```
260
+
261
+ **Gamut mapping strategies:**
262
+
263
+ ```ts
264
+ import { toGamut, clampChroma, oklch } from 'culori';
265
+
266
+ const outOfGamut = oklch('oklch(0.5 0.37 290)');
267
+
268
+ // Strategy 1: Clip chroma until color fits sRGB (preserves hue + lightness)
269
+ const strategy1 = toGamut('srgb', 'oklch', 'clip')(outOfGamut);
270
+
271
+ // Strategy 2: Gamut map with CSS-compatible algorithm
272
+ const strategy2 = toGamut('srgb', 'oklch')(outOfGamut);
273
+
274
+ // Strategy 3: Manual chroma reduction
275
+ const manual = { ...outOfGamut, c: 0.25 }; // Reduce chroma until in gamut
276
+ ```
277
+
278
+ **Detection:**
279
+
280
+ ```css
281
+ /* Use @supports to detect OKLCH support and provide fallback */
282
+ .hero {
283
+ background: #6b5ae8; /* sRGB fallback */
284
+ background: oklch(0.5 0.3 280); /* OKLCH — wider gamut */
285
+ }
286
+
287
+ /* Also detect display gamut */
288
+ @media (color-gamut: p3) {
289
+ .hero {
290
+ background: oklch(0.5 0.3 280);
291
+ }
292
+ }
293
+ ```
294
+
295
+ ---
296
+
297
+ ## 7. Tailwind v4 Migration
298
+
299
+ ### Tailwind v4 `@theme` with OKLCH
300
+
301
+ ```css
302
+ /* app.css — Tailwind v4 theme with OKLCH */
303
+ @import "tailwindcss";
304
+
305
+ @theme {
306
+ --color-primary-50: oklch(0.97 0.01 260);
307
+ --color-primary-100: oklch(0.93 0.03 260);
308
+ --color-primary-200: oklch(0.86 0.06 260);
309
+ --color-primary-300: oklch(0.78 0.10 260);
310
+ --color-primary-400: oklch(0.68 0.15 260);
311
+ --color-primary-500: oklch(0.56 0.18 260);
312
+ --color-primary-600: oklch(0.45 0.18 260);
313
+ --color-primary-700: oklch(0.35 0.15 260);
314
+ --color-primary-800: oklch(0.27 0.12 260);
315
+ --color-primary-900: oklch(0.20 0.08 260);
316
+ --color-primary-950: oklch(0.14 0.04 260);
317
+
318
+ --color-surface: oklch(0.99 0 0);
319
+ --color-surface-alt: oklch(0.96 0.02 260);
320
+ --color-border: oklch(0.88 0.01 260);
321
+ --color-muted: oklch(0.45 0.01 260);
322
+ }
323
+ ```
324
+
325
+ ### Dark mode with OKLCH
326
+
327
+ OKLCH makes dark mode trivial — just adjust L (lightness) while keeping H and C the same:
328
+
329
+ ```css
330
+ @custom-variant dark (&:where(.dark, .dark *));
331
+
332
+ @theme {
333
+ /* Light — high L for surfaces */
334
+ --color-surface: oklch(0.99 0 0);
335
+ --color-surface-alt: oklch(0.96 0.02 260);
336
+
337
+ /* Dark — low L for surfaces */
338
+ --color-surface-dark: oklch(0.12 0 0);
339
+ --color-surface-alt-dark: oklch(0.15 0.02 260);
340
+ }
341
+
342
+ /* Or use CSS variables with media query: */
343
+ :root {
344
+ --bg: oklch(0.99 0 0);
345
+ --text: oklch(0.15 0 0);
346
+ }
347
+
348
+ @media (prefers-color-scheme: dark) {
349
+ :root {
350
+ --bg: oklch(0.12 0 0);
351
+ --text: oklch(0.88 0 0);
352
+ }
353
+ }
354
+
355
+ /* The brand color stays the same — only background/text flip */
356
+ /* primary-500 remains oklch(0.56 0.18 260) in both modes */
357
+ ```
358
+
359
+ ### Tinted neutrals
360
+
361
+ Give your neutral tones a subtle brand hue by adding very low chroma:
362
+
363
+ ```css
364
+ /* BEFORE — true neutral grays (bland) */
365
+ --gray-100: oklch(0.96 0 0);
366
+ --gray-500: oklch(0.55 0 0);
367
+
368
+ /* AFTER — tinted with brand hue (sophisticated) */
369
+ /* C=0.01 is imperceptible as "color" but reads as a richer neutral */
370
+ --gray-100: oklch(0.96 0.01 260); /* Barely blue-tinted */
371
+ --gray-500: oklch(0.55 0.02 260); /* Subtle warmth from brand hue */
372
+ ```
373
+
374
+ ### Migration from HEX/RGB
375
+
376
+ ```css
377
+ /* BEFORE — Tailwind v3, HEX colors */
378
+ :root {
379
+ --color-primary: #3B82F6;
380
+ --color-surface: #FFFFFF;
381
+ --color-muted: #6B7280;
382
+ }
383
+
384
+ /* AFTER — Tailwind v4, OKLCH */
385
+ :root {
386
+ --color-primary: oklch(0.56 0.18 260);
387
+ --color-surface: oklch(1 0 0);
388
+ --color-muted: oklch(0.45 0.01 260);
389
+ }
390
+ ```
391
+
392
+ **Migration checklist:**
393
+ 1. Convert all HEX colors to OKLCH using a converter tool
394
+ 2. Update Tailwind v4 `@theme` with OKLCH values
395
+ 3. Keep HEX fallbacks for older browsers if needed
396
+ 4. Adjust chroma values — high-chroma HEX colors may need C reduction
397
+ 5. Verify contrast ratios — OKLCH reveals contrast issues that HEX masked
398
+ 6. Test dark mode — OKLCH's uniform L makes this trivial
399
+
400
+ ---
401
+
402
+ ## Don't
403
+
404
+ | Pattern | Replacement | Because |
405
+ |---------|-------------|---------|
406
+ | C too high for text | Keep text C ≤ 0.15; reserve C≥0.2 for display/brand use | High chroma text is fatiguing at small sizes |
407
+ | Not checking gamut | Reduce C or use `toGamut()` for predictable clipping | Colors outside sRGB clip unpredictably |
408
+ | Mixing color spaces in palette | Use one color space for the entire palette | Mixed spaces make comparison impossible |
409
+ | C=0 for all grays | Use C=0.01 with brand hue for richer neutrals | True neutral grays can look sterile |
410
+ | No fallback for OKLCH | Use `color: #hex; color: oklch(...)` pattern | OKLCH unsupported in older browsers |
411
+
412
+ ---
413
+
414
+ ## Verification
415
+
416
+ - [ ] All colors use OKLCH syntax (`oklch(L C H / alpha)`)
417
+ - [ ] Grays and neutrals use C=0 to C=0.02 range
418
+ - [ ] Accent colors use C=0.15 to C=0.25 range
419
+ - [ ] Text colors do not exceed C=0.15 (readability)
420
+ - [ ] Contrast ratio checked: |ΔL| ≥ 0.35 for body text, ≥ 0.25 for large text
421
+ - [ ] Gamut checked: no colors outside sRGB without fallback
422
+ - [ ] HEX/RGB fallback provided for colors where gamut mapping matters
423
+ - [ ] Palette follows natural chroma curve (peak at 500/600, low at 50/950)
424
+ - [ ] Neutrals are tinted with brand hue (C ≥ 0.01) for richness
425
+ - [ ] Tailwind v4 `@theme` uses OKLCH values
426
+ - [ ] Dark mode flips L values while keeping H and C consistent