@lifeonlars/prime-yggdrasil 0.2.6 → 0.3.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/.ai/agents/accessibility.md +581 -0
- package/.ai/agents/block-composer.md +909 -0
- package/.ai/agents/drift-validator.md +784 -0
- package/.ai/agents/interaction-patterns.md +465 -0
- package/.ai/agents/primeflex-guard.md +815 -0
- package/.ai/agents/semantic-token-intent.md +739 -0
- package/README.md +139 -12
- package/cli/bin/yggdrasil.js +134 -0
- package/cli/commands/audit.js +425 -0
- package/cli/commands/init.js +288 -0
- package/cli/commands/validate.js +405 -0
- package/cli/templates/.ai/yggdrasil/README.md +308 -0
- package/docs/AESTHETICS.md +168 -0
- package/docs/PRIMEFLEX-POLICY.md +737 -0
- package/package.json +6 -1
- package/docs/Fixes.md +0 -258
- package/docs/archive/README.md +0 -27
- package/docs/archive/SEMANTIC-MIGRATION-PLAN.md +0 -177
- package/docs/archive/YGGDRASIL_THEME.md +0 -264
- package/docs/archive/agentic_policy.md +0 -216
- package/docs/contrast-report.md +0 -9
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
# PrimeFlex Usage Policy
|
|
2
|
+
|
|
3
|
+
**Purpose:** Define the allowed and forbidden uses of PrimeFlex utility classes in the Yggdrasil design system.
|
|
4
|
+
|
|
5
|
+
**TL;DR:** PrimeFlex for **layout and spacing only**. Never for design (colors, borders, shadows). Never on PrimeReact components (except `w-full` on inputs).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Principle: Layout vs Design
|
|
10
|
+
|
|
11
|
+
The fundamental rule of PrimeFlex usage is the separation of **layout** and **design** concerns.
|
|
12
|
+
|
|
13
|
+
### Layout (✅ PrimeFlex)
|
|
14
|
+
|
|
15
|
+
**What it controls:**
|
|
16
|
+
- Element positioning (flex, grid, relative, absolute)
|
|
17
|
+
- Spacing between elements (padding, margin, gap)
|
|
18
|
+
- Element alignment (justify-content, align-items)
|
|
19
|
+
- Structural dimensions (width, height for layout purposes)
|
|
20
|
+
- Display properties (block, inline-block, hidden)
|
|
21
|
+
|
|
22
|
+
**Why PrimeFlex?**
|
|
23
|
+
- Layout changes with screen size (responsive)
|
|
24
|
+
- Layout changes with component composition
|
|
25
|
+
- Layout is structural, not thematic
|
|
26
|
+
- Layout doesn't need theme awareness
|
|
27
|
+
|
|
28
|
+
**Example:**
|
|
29
|
+
```tsx
|
|
30
|
+
<div className="flex justify-content-between gap-3 p-4">
|
|
31
|
+
<span>User Name</span>
|
|
32
|
+
<Button label="Edit" />
|
|
33
|
+
</div>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### Design (❌ PrimeFlex → ✅ Semantic Tokens)
|
|
39
|
+
|
|
40
|
+
**What it controls:**
|
|
41
|
+
- Colors (text, background, border)
|
|
42
|
+
- Borders (width, style, radius)
|
|
43
|
+
- Shadows (elevation)
|
|
44
|
+
- Typography (font family, size, weight)
|
|
45
|
+
- Visual effects (opacity, transitions)
|
|
46
|
+
|
|
47
|
+
**Why semantic tokens?**
|
|
48
|
+
- Design changes with theme (light/dark mode)
|
|
49
|
+
- Design changes with brand updates
|
|
50
|
+
- Design requires contrast validation
|
|
51
|
+
- Design must be accessible
|
|
52
|
+
|
|
53
|
+
**Example:**
|
|
54
|
+
```tsx
|
|
55
|
+
<div style={{
|
|
56
|
+
color: 'var(--text-neutral-default)',
|
|
57
|
+
background: 'var(--surface-neutral-secondary)',
|
|
58
|
+
border: `1px solid var(--border-neutral-default)`,
|
|
59
|
+
borderRadius: 'var(--radius-md)',
|
|
60
|
+
boxShadow: 'var(--elevation-subtle)'
|
|
61
|
+
}}>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## The Allowlist
|
|
67
|
+
|
|
68
|
+
### Flex Layout ✅
|
|
69
|
+
|
|
70
|
+
**Purpose:** Flexbox positioning and alignment
|
|
71
|
+
|
|
72
|
+
```css
|
|
73
|
+
/* Container */
|
|
74
|
+
flex
|
|
75
|
+
flex-1, flex-auto, flex-initial, flex-none
|
|
76
|
+
|
|
77
|
+
/* Direction */
|
|
78
|
+
flex-row, flex-row-reverse
|
|
79
|
+
flex-column, flex-column-reverse
|
|
80
|
+
|
|
81
|
+
/* Wrapping */
|
|
82
|
+
flex-wrap, flex-nowrap, flex-wrap-reverse
|
|
83
|
+
|
|
84
|
+
/* Main axis alignment */
|
|
85
|
+
justify-content-start
|
|
86
|
+
justify-content-end
|
|
87
|
+
justify-content-center
|
|
88
|
+
justify-content-between
|
|
89
|
+
justify-content-around
|
|
90
|
+
justify-content-evenly
|
|
91
|
+
|
|
92
|
+
/* Cross axis alignment */
|
|
93
|
+
align-items-start
|
|
94
|
+
align-items-end
|
|
95
|
+
align-items-center
|
|
96
|
+
align-items-baseline
|
|
97
|
+
align-items-stretch
|
|
98
|
+
|
|
99
|
+
/* Multi-line alignment */
|
|
100
|
+
align-content-start
|
|
101
|
+
align-content-end
|
|
102
|
+
align-content-center
|
|
103
|
+
align-content-between
|
|
104
|
+
align-content-around
|
|
105
|
+
align-content-stretch
|
|
106
|
+
|
|
107
|
+
/* Item self-alignment */
|
|
108
|
+
align-self-auto
|
|
109
|
+
align-self-start
|
|
110
|
+
align-self-end
|
|
111
|
+
align-self-center
|
|
112
|
+
align-self-baseline
|
|
113
|
+
align-self-stretch
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Usage:**
|
|
117
|
+
```tsx
|
|
118
|
+
✅ <div className="flex justify-content-between align-items-center">
|
|
119
|
+
✅ <div className="flex-column gap-2">
|
|
120
|
+
✅ <div className="flex-1"> {/* Flexible width */}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### Grid Layout ✅
|
|
126
|
+
|
|
127
|
+
**Purpose:** CSS Grid positioning
|
|
128
|
+
|
|
129
|
+
```css
|
|
130
|
+
/* Grid container */
|
|
131
|
+
grid
|
|
132
|
+
|
|
133
|
+
/* Columns (12-column system) */
|
|
134
|
+
col, col-1, col-2, ..., col-12
|
|
135
|
+
|
|
136
|
+
/* Column offset */
|
|
137
|
+
col-offset-1, col-offset-2, ..., col-offset-12
|
|
138
|
+
|
|
139
|
+
/* Responsive columns */
|
|
140
|
+
sm:col-*, md:col-*, lg:col-*, xl:col-*
|
|
141
|
+
|
|
142
|
+
/* Gap (flex and grid) */
|
|
143
|
+
gap-0, gap-1, gap-2, gap-3, gap-4, gap-5, gap-6, gap-7, gap-8
|
|
144
|
+
column-gap-*, row-gap-*
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Usage:**
|
|
148
|
+
```tsx
|
|
149
|
+
✅ <div className="grid">
|
|
150
|
+
<div className="col-12 md:col-6 lg:col-4">
|
|
151
|
+
Card 1
|
|
152
|
+
</div>
|
|
153
|
+
<div className="col-12 md:col-6 lg:col-4">
|
|
154
|
+
Card 2
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
✅ <div className="grid gap-3"> {/* 12px gap */}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Spacing ✅
|
|
164
|
+
|
|
165
|
+
**Purpose:** Padding, margin, and gap (4px grid increments)
|
|
166
|
+
|
|
167
|
+
**Spacing Scale:**
|
|
168
|
+
- `0` = 0px
|
|
169
|
+
- `1` = 0.25rem (4px)
|
|
170
|
+
- `2` = 0.5rem (8px)
|
|
171
|
+
- `3` = 0.75rem (12px)
|
|
172
|
+
- `4` = 1rem (16px)
|
|
173
|
+
- `5` = 1.25rem (20px)
|
|
174
|
+
- `6` = 1.5rem (24px)
|
|
175
|
+
- `7` = 2rem (32px)
|
|
176
|
+
- `8` = 3rem (48px)
|
|
177
|
+
|
|
178
|
+
**Classes:**
|
|
179
|
+
```css
|
|
180
|
+
/* Padding */
|
|
181
|
+
p-0, p-1, p-2, p-3, p-4, p-5, p-6, p-7, p-8
|
|
182
|
+
pt-*, pr-*, pb-*, pl-* /* Directional */
|
|
183
|
+
px-*, py-* /* Axis */
|
|
184
|
+
|
|
185
|
+
/* Margin */
|
|
186
|
+
m-0, m-1, m-2, m-3, m-4, m-5, m-6, m-7, m-8
|
|
187
|
+
mt-*, mr-*, mb-*, ml-* /* Directional */
|
|
188
|
+
mx-*, my-* /* Axis */
|
|
189
|
+
m-auto /* Auto margin */
|
|
190
|
+
|
|
191
|
+
/* Gap (already covered in flex/grid) */
|
|
192
|
+
gap-0, gap-1, gap-2, gap-3, gap-4, gap-5, gap-6, gap-7, gap-8
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Usage:**
|
|
196
|
+
```tsx
|
|
197
|
+
✅ <div className="p-4"> {/* 16px padding */}
|
|
198
|
+
✅ <div className="px-3 py-2"> {/* 12px horizontal, 8px vertical */}
|
|
199
|
+
✅ <div className="mt-2 mb-4"> {/* 8px top, 16px bottom margin */}
|
|
200
|
+
✅ <div className="gap-3"> {/* 12px gap between children */}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### Display ✅
|
|
206
|
+
|
|
207
|
+
**Purpose:** Element display type and visibility
|
|
208
|
+
|
|
209
|
+
```css
|
|
210
|
+
/* Display type */
|
|
211
|
+
block
|
|
212
|
+
inline-block
|
|
213
|
+
inline
|
|
214
|
+
|
|
215
|
+
/* Visibility */
|
|
216
|
+
hidden /* display: none */
|
|
217
|
+
invisible /* visibility: hidden */
|
|
218
|
+
|
|
219
|
+
/* Overflow */
|
|
220
|
+
overflow-auto, overflow-hidden, overflow-visible, overflow-scroll
|
|
221
|
+
overflow-x-auto, overflow-x-hidden, overflow-x-scroll
|
|
222
|
+
overflow-y-auto, overflow-y-hidden, overflow-y-scroll
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Usage:**
|
|
226
|
+
```tsx
|
|
227
|
+
✅ <div className="hidden md:block"> {/* Responsive visibility */}
|
|
228
|
+
✅ <div className="overflow-auto"> {/* Scrollable */}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Positioning ✅
|
|
234
|
+
|
|
235
|
+
**Purpose:** Element positioning
|
|
236
|
+
|
|
237
|
+
```css
|
|
238
|
+
/* Position type */
|
|
239
|
+
static, relative, absolute, fixed, sticky
|
|
240
|
+
|
|
241
|
+
/* Position values */
|
|
242
|
+
top-0, top-50, top-100
|
|
243
|
+
right-0, right-50, right-100
|
|
244
|
+
bottom-0, bottom-50, bottom-100
|
|
245
|
+
left-0, left-50, left-100
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Usage:**
|
|
249
|
+
```tsx
|
|
250
|
+
✅ <div className="relative">
|
|
251
|
+
<div className="absolute top-0 right-0">
|
|
252
|
+
Badge
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### Sizing ✅
|
|
260
|
+
|
|
261
|
+
**Purpose:** Width and height (structural only)
|
|
262
|
+
|
|
263
|
+
```css
|
|
264
|
+
/* Width */
|
|
265
|
+
w-auto, w-full
|
|
266
|
+
w-screen /* 100vw */
|
|
267
|
+
w-1, w-2, w-3, w-4, w-5, w-6 /* Fixed sizes */
|
|
268
|
+
|
|
269
|
+
/* Height */
|
|
270
|
+
h-auto, h-full
|
|
271
|
+
h-screen /* 100vh */
|
|
272
|
+
h-1, h-2, h-3, h-4, h-5, h-6 /* Fixed sizes */
|
|
273
|
+
|
|
274
|
+
/* Min/Max */
|
|
275
|
+
min-w-0, max-w-full
|
|
276
|
+
min-h-0, max-h-full
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Usage:**
|
|
280
|
+
```tsx
|
|
281
|
+
✅ <div className="w-full"> {/* Full width */}
|
|
282
|
+
✅ <div className="h-screen"> {/* Full viewport height */}
|
|
283
|
+
✅ <InputText className="w-full" /> {/* Full-width input (exception) */}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## The Forbidden List
|
|
289
|
+
|
|
290
|
+
### ❌ Colors
|
|
291
|
+
|
|
292
|
+
**Forbidden:** All color utilities
|
|
293
|
+
|
|
294
|
+
```css
|
|
295
|
+
/* Text colors */
|
|
296
|
+
text-blue-500, text-red-600, text-gray-900, text-white, etc.
|
|
297
|
+
|
|
298
|
+
/* Background colors */
|
|
299
|
+
bg-blue-500, bg-white, bg-gray-100, etc.
|
|
300
|
+
|
|
301
|
+
/* Border colors */
|
|
302
|
+
border-blue-500, border-gray-300, etc.
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Use Instead:** Semantic tokens
|
|
306
|
+
```tsx
|
|
307
|
+
✅ color: 'var(--text-neutral-default)'
|
|
308
|
+
✅ background: 'var(--surface-brand-primary)'
|
|
309
|
+
✅ borderColor: 'var(--border-neutral-default)'
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Rationale:**
|
|
313
|
+
- Colors must adapt to light/dark mode
|
|
314
|
+
- Colors must meet contrast requirements
|
|
315
|
+
- Colors define brand identity (centralized in theme)
|
|
316
|
+
- Hardcoded colors break theme consistency
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### ❌ Borders
|
|
321
|
+
|
|
322
|
+
**Forbidden:** Border utilities
|
|
323
|
+
|
|
324
|
+
```css
|
|
325
|
+
/* Border width */
|
|
326
|
+
border, border-0, border-2, border-4, border-8
|
|
327
|
+
|
|
328
|
+
/* Border style */
|
|
329
|
+
border-solid, border-dashed, border-dotted, border-double
|
|
330
|
+
|
|
331
|
+
/* Border sides */
|
|
332
|
+
border-t, border-r, border-b, border-l
|
|
333
|
+
border-x, border-y
|
|
334
|
+
|
|
335
|
+
/* Border radius */
|
|
336
|
+
rounded, rounded-sm, rounded-md, rounded-lg, rounded-xl, rounded-full
|
|
337
|
+
rounded-t-*, rounded-r-*, rounded-b-*, rounded-l-*
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Use Instead:** Semantic tokens
|
|
341
|
+
```tsx
|
|
342
|
+
✅ border: `1px solid var(--border-neutral-default)`
|
|
343
|
+
✅ borderRadius: 'var(--radius-md)'
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Exception:** `1px` hairline borders are allowed for width
|
|
347
|
+
```tsx
|
|
348
|
+
✅ border: '1px solid var(--border-neutral-default)' {/* 1px OK */}
|
|
349
|
+
❌ border: '3px solid var(--border-brand-primary)' {/* 3px NOT OK */}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Rationale:**
|
|
353
|
+
- Border styles are part of design language (theme)
|
|
354
|
+
- Border radius values must be consistent (design tokens)
|
|
355
|
+
- Theme defines visual hierarchy through borders
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
### ❌ Shadows
|
|
360
|
+
|
|
361
|
+
**Forbidden:** Shadow utilities
|
|
362
|
+
|
|
363
|
+
```css
|
|
364
|
+
shadow-none
|
|
365
|
+
shadow-sm
|
|
366
|
+
shadow, shadow-md
|
|
367
|
+
shadow-lg, shadow-xl
|
|
368
|
+
shadow-2xl
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Use Instead:** Elevation tokens
|
|
372
|
+
```tsx
|
|
373
|
+
✅ boxShadow: 'var(--elevation-subtle)'
|
|
374
|
+
✅ boxShadow: 'var(--elevation-moderate)'
|
|
375
|
+
✅ boxShadow: 'var(--elevation-elevated)'
|
|
376
|
+
✅ boxShadow: 'var(--elevation-high)'
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Rationale:**
|
|
380
|
+
- Shadows define depth hierarchy (design system)
|
|
381
|
+
- Dark mode requires different shadow strategies
|
|
382
|
+
- Elevation is semantic (subtle vs elevated)
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
### ❌ Typography
|
|
387
|
+
|
|
388
|
+
**Forbidden:** Typography utilities
|
|
389
|
+
|
|
390
|
+
```css
|
|
391
|
+
/* Font family */
|
|
392
|
+
font-sans, font-serif, font-mono
|
|
393
|
+
|
|
394
|
+
/* Font size */
|
|
395
|
+
text-xs, text-sm, text-base, text-lg, text-xl, text-2xl, etc.
|
|
396
|
+
|
|
397
|
+
/* Font weight */
|
|
398
|
+
font-thin, font-light, font-normal, font-medium, font-semibold, font-bold, font-black
|
|
399
|
+
|
|
400
|
+
/* Text alignment */
|
|
401
|
+
text-left, text-center, text-right, text-justify
|
|
402
|
+
|
|
403
|
+
/* Text decoration */
|
|
404
|
+
underline, no-underline, line-through
|
|
405
|
+
|
|
406
|
+
/* Text transform */
|
|
407
|
+
uppercase, lowercase, capitalize
|
|
408
|
+
|
|
409
|
+
/* Line height */
|
|
410
|
+
leading-none, leading-tight, leading-normal, leading-relaxed
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**Use Instead:** CSS styles with theme values
|
|
414
|
+
```tsx
|
|
415
|
+
✅ fontSize: '1.125rem' {/* 18px */}
|
|
416
|
+
✅ fontWeight: 600
|
|
417
|
+
✅ textAlign: 'center'
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Rationale:**
|
|
421
|
+
- Typography is core to brand identity
|
|
422
|
+
- Font sizes must follow typographic scale
|
|
423
|
+
- Font weights are limited by font family
|
|
424
|
+
- Theme owns typography hierarchy
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
### ❌ Tailwind-Specific Utilities
|
|
429
|
+
|
|
430
|
+
**Forbidden:** These are Tailwind, not PrimeFlex
|
|
431
|
+
|
|
432
|
+
```css
|
|
433
|
+
/* Spacing utilities */
|
|
434
|
+
space-x-*, space-y-*
|
|
435
|
+
divide-x-*, divide-y-*
|
|
436
|
+
|
|
437
|
+
/* Ring utilities */
|
|
438
|
+
ring-*, ring-offset-*, ring-inset
|
|
439
|
+
|
|
440
|
+
/* Effects */
|
|
441
|
+
blur-*, brightness-*, contrast-*, grayscale-*, hue-rotate-*
|
|
442
|
+
|
|
443
|
+
/* Transitions */
|
|
444
|
+
transition-*, duration-*, delay-*, ease-*
|
|
445
|
+
|
|
446
|
+
/* Transforms */
|
|
447
|
+
transform, translate-*, rotate-*, scale-*, skew-*
|
|
448
|
+
|
|
449
|
+
/* Filters */
|
|
450
|
+
filter, backdrop-filter
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Use Instead:**
|
|
454
|
+
- `space-x/y` → PrimeFlex `gap-*`
|
|
455
|
+
- `ring` → CSS `outline` with semantic token
|
|
456
|
+
- Effects/transitions → CSS properties
|
|
457
|
+
- Transforms → CSS properties
|
|
458
|
+
|
|
459
|
+
**Rationale:**
|
|
460
|
+
- Yggdrasil uses PrimeFlex, not Tailwind
|
|
461
|
+
- Different API, different philosophy
|
|
462
|
+
- Prevents confusion and mixing frameworks
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Critical Anti-Pattern: PrimeFlex on Components
|
|
467
|
+
|
|
468
|
+
### The Rule
|
|
469
|
+
|
|
470
|
+
**✅ PrimeFlex on containers (div, section, etc.)**
|
|
471
|
+
**❌ PrimeFlex on PrimeReact components**
|
|
472
|
+
|
|
473
|
+
### Why This Matters
|
|
474
|
+
|
|
475
|
+
PrimeReact components are **designed and themed**. They include:
|
|
476
|
+
- Semantic structure (proper HTML elements)
|
|
477
|
+
- Accessibility features (ARIA attributes)
|
|
478
|
+
- Interactive states (hover, focus, active, disabled)
|
|
479
|
+
- Theme integration (colors, borders, shadows)
|
|
480
|
+
|
|
481
|
+
Adding utility classes **overrides the theme** and breaks:
|
|
482
|
+
- Theme consistency
|
|
483
|
+
- Light/dark mode switching
|
|
484
|
+
- Accessibility considerations
|
|
485
|
+
- Responsive design
|
|
486
|
+
|
|
487
|
+
### The One Exception
|
|
488
|
+
|
|
489
|
+
**`w-full` is allowed on form inputs** for responsive sizing:
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
✅ <InputText className="w-full" placeholder="Email" />
|
|
493
|
+
✅ <Dropdown className="w-full" options={options} />
|
|
494
|
+
✅ <Calendar className="w-full" />
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**Everything else is forbidden:**
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
❌ <Button className="bg-blue-500 p-3" label="Submit" />
|
|
501
|
+
❌ <DataTable className="border shadow-lg" value={data} />
|
|
502
|
+
❌ <Card className="rounded-xl bg-white">
|
|
503
|
+
❌ <InputText className="border-2 rounded-lg" /> {/* Beyond w-full */}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Correct Pattern
|
|
507
|
+
|
|
508
|
+
**Wrap components in containers for layout:**
|
|
509
|
+
|
|
510
|
+
```tsx
|
|
511
|
+
✅ <div className="flex gap-2 p-4">
|
|
512
|
+
<Button label="Save" />
|
|
513
|
+
<Button label="Cancel" outlined />
|
|
514
|
+
</div>
|
|
515
|
+
|
|
516
|
+
❌ <div>
|
|
517
|
+
<Button className="mr-2 mb-2" label="Save" />
|
|
518
|
+
<Button label="Cancel" outlined />
|
|
519
|
+
</div>
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Use component props for styling:**
|
|
523
|
+
|
|
524
|
+
```tsx
|
|
525
|
+
✅ <Button severity="danger" label="Delete" /> {/* Use props */}
|
|
526
|
+
❌ <Button className="bg-red-500" label="Delete" /> {/* Don't override */}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Rationale: Why This Policy Exists
|
|
532
|
+
|
|
533
|
+
### Problem: Tailwind Reinvention
|
|
534
|
+
|
|
535
|
+
Without constraints, developers (and AI agents) will reinvent Tailwind using PrimeFlex:
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
❌ {/* This is Tailwind-style development with PrimeFlex */}
|
|
539
|
+
<div className="bg-blue-500 text-white p-4 rounded-lg shadow-md border border-blue-600">
|
|
540
|
+
<Button className="bg-red-500 text-white font-bold py-2 px-4 rounded-full">
|
|
541
|
+
Delete
|
|
542
|
+
</Button>
|
|
543
|
+
</div>
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**Problems:**
|
|
547
|
+
- No theme consistency (hardcoded colors)
|
|
548
|
+
- No dark mode support (static colors)
|
|
549
|
+
- No accessibility validation (arbitrary contrast)
|
|
550
|
+
- Duplication (same styling copy-pasted)
|
|
551
|
+
- Unmaintainable (can't update design globally)
|
|
552
|
+
|
|
553
|
+
### Solution: Separation of Concerns
|
|
554
|
+
|
|
555
|
+
**PrimeFlex:** Layout and spacing (structure)
|
|
556
|
+
**Semantic Tokens:** Design and theming (appearance)
|
|
557
|
+
**PrimeReact:** Components (functionality)
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
✅ {/* Correct: Separation of concerns */}
|
|
561
|
+
<div
|
|
562
|
+
className="flex flex-column gap-3 p-4" {/* PrimeFlex: layout */}
|
|
563
|
+
style={{
|
|
564
|
+
background: 'var(--surface-neutral-secondary)', {/* Tokens: design */}
|
|
565
|
+
borderRadius: 'var(--radius-md)',
|
|
566
|
+
boxShadow: 'var(--elevation-subtle)'
|
|
567
|
+
}}
|
|
568
|
+
>
|
|
569
|
+
<Button severity="danger" label="Delete" /> {/* PrimeReact: component */}
|
|
570
|
+
</div>
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Benefits:**
|
|
574
|
+
- Theme consistency (centralized design)
|
|
575
|
+
- Dark mode support (semantic tokens adapt)
|
|
576
|
+
- Accessibility guaranteed (validated token pairings)
|
|
577
|
+
- Maintainability (update theme, not code)
|
|
578
|
+
- Reusability (extract to Blocks)
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## Migration Guide from Tailwind
|
|
583
|
+
|
|
584
|
+
If you're coming from Tailwind, here's the mapping:
|
|
585
|
+
|
|
586
|
+
### Layout → Keep (use PrimeFlex equivalents)
|
|
587
|
+
|
|
588
|
+
| Tailwind | PrimeFlex |
|
|
589
|
+
|----------|-----------|
|
|
590
|
+
| `flex` | `flex` |
|
|
591
|
+
| `flex-col` | `flex-column` |
|
|
592
|
+
| `justify-between` | `justify-content-between` |
|
|
593
|
+
| `items-center` | `align-items-center` |
|
|
594
|
+
| `gap-4` | `gap-3` (12px) |
|
|
595
|
+
| `p-4` | `p-3` (12px) |
|
|
596
|
+
| `grid grid-cols-3` | `grid` + `col-4` (12/3=4) |
|
|
597
|
+
| `space-x-2` | `flex gap-2` |
|
|
598
|
+
| `hidden md:block` | `hidden md:block` |
|
|
599
|
+
|
|
600
|
+
### Design → Replace with semantic tokens
|
|
601
|
+
|
|
602
|
+
| Tailwind | Yggdrasil |
|
|
603
|
+
|----------|-----------|
|
|
604
|
+
| `bg-blue-500` | `background: var(--surface-brand-primary)` |
|
|
605
|
+
| `text-gray-900` | `color: var(--text-neutral-default)` |
|
|
606
|
+
| `border-gray-300` | `border: 1px solid var(--border-neutral-default)` |
|
|
607
|
+
| `rounded-lg` | `borderRadius: var(--radius-lg)` |
|
|
608
|
+
| `shadow-md` | `boxShadow: var(--elevation-moderate)` |
|
|
609
|
+
| `text-white` | `color: var(--text-onsurface-onbrand)` |
|
|
610
|
+
|
|
611
|
+
### Components → Use PrimeReact
|
|
612
|
+
|
|
613
|
+
| Tailwind Pattern | Yggdrasil |
|
|
614
|
+
|------------------|-----------|
|
|
615
|
+
| Custom button with `bg-blue-500` | `<Button label="..." />` |
|
|
616
|
+
| Custom input with `border rounded` | `<InputText />` |
|
|
617
|
+
| Custom card with `border shadow` | `<Card>` |
|
|
618
|
+
| Custom table | `<DataTable>` |
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## Common Violations & Fixes
|
|
623
|
+
|
|
624
|
+
### Violation 1: Colored Card
|
|
625
|
+
|
|
626
|
+
❌ **Wrong:**
|
|
627
|
+
```tsx
|
|
628
|
+
<div className="bg-white border border-gray-200 rounded-lg shadow-md p-6">
|
|
629
|
+
<h2 className="text-gray-900 font-bold text-xl mb-4">
|
|
630
|
+
Card Title
|
|
631
|
+
</h2>
|
|
632
|
+
<p className="text-gray-600">
|
|
633
|
+
Card content
|
|
634
|
+
</p>
|
|
635
|
+
</div>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
✅ **Correct:**
|
|
639
|
+
```tsx
|
|
640
|
+
<Card className="p-4"> {/* or just <Card> */}
|
|
641
|
+
<h2 style={{
|
|
642
|
+
color: 'var(--text-neutral-loud)',
|
|
643
|
+
fontSize: '1.25rem',
|
|
644
|
+
fontWeight: 600,
|
|
645
|
+
marginBottom: '1rem'
|
|
646
|
+
}}>
|
|
647
|
+
Card Title
|
|
648
|
+
</h2>
|
|
649
|
+
<p style={{ color: 'var(--text-neutral-subdued)' }}>
|
|
650
|
+
Card content
|
|
651
|
+
</p>
|
|
652
|
+
</Card>
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
**Or even simpler - just use Card:**
|
|
656
|
+
```tsx
|
|
657
|
+
<Card title="Card Title">
|
|
658
|
+
<p>Card content</p>
|
|
659
|
+
</Card>
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
### Violation 2: Styled Button
|
|
665
|
+
|
|
666
|
+
❌ **Wrong:**
|
|
667
|
+
```tsx
|
|
668
|
+
<button className="bg-red-500 text-white font-bold py-2 px-4 rounded-full hover:bg-red-600">
|
|
669
|
+
Delete
|
|
670
|
+
</button>
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
✅ **Correct:**
|
|
674
|
+
```tsx
|
|
675
|
+
<Button severity="danger" label="Delete" icon="pi pi-trash" />
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
### Violation 3: Form Layout
|
|
681
|
+
|
|
682
|
+
❌ **Wrong:**
|
|
683
|
+
```tsx
|
|
684
|
+
<div className="space-y-4">
|
|
685
|
+
<input className="border border-gray-300 rounded-lg px-3 py-2 w-full" />
|
|
686
|
+
<input className="border border-gray-300 rounded-lg px-3 py-2 w-full" />
|
|
687
|
+
<button className="bg-blue-500 text-white px-4 py-2 rounded w-full">
|
|
688
|
+
Submit
|
|
689
|
+
</button>
|
|
690
|
+
</div>
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
✅ **Correct:**
|
|
694
|
+
```tsx
|
|
695
|
+
<div className="flex flex-column gap-3">
|
|
696
|
+
<InputText className="w-full" />
|
|
697
|
+
<InputText className="w-full" />
|
|
698
|
+
<Button label="Submit" />
|
|
699
|
+
</div>
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
## Enforcement
|
|
705
|
+
|
|
706
|
+
This policy is enforced through:
|
|
707
|
+
|
|
708
|
+
1. **PrimeFlex Guard Agent** - Guides developers during implementation
|
|
709
|
+
2. **ESLint Plugin** - Code-time detection in IDE
|
|
710
|
+
- Rule: `@lifeonlars/yggdrasil/primeflex-allowlist`
|
|
711
|
+
- Rule: `@lifeonlars/yggdrasil/no-utility-on-components`
|
|
712
|
+
3. **Drift Validator CLI** - Pre-commit and CI/CD validation
|
|
713
|
+
4. **Code Review** - Manual review for complex cases
|
|
714
|
+
|
|
715
|
+
**Severity:**
|
|
716
|
+
- Using forbidden PrimeFlex (colors, etc.) → Warning
|
|
717
|
+
- PrimeFlex on PrimeReact components → **Error** (Critical)
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## Summary
|
|
722
|
+
|
|
723
|
+
**DO:**
|
|
724
|
+
- ✅ Use PrimeFlex for layout (flex, grid, positioning)
|
|
725
|
+
- ✅ Use PrimeFlex for spacing (padding, margin, gap) on 4px grid
|
|
726
|
+
- ✅ Use PrimeFlex on containers (div, section, article)
|
|
727
|
+
- ✅ Use semantic tokens for design (colors, borders, shadows)
|
|
728
|
+
- ✅ Use PrimeReact components for UI elements
|
|
729
|
+
|
|
730
|
+
**DON'T:**
|
|
731
|
+
- ❌ Use PrimeFlex for colors, borders, shadows, typography
|
|
732
|
+
- ❌ Use PrimeFlex on PrimeReact components (except w-full on inputs)
|
|
733
|
+
- ❌ Use Tailwind classes
|
|
734
|
+
- ❌ Hardcode design values
|
|
735
|
+
- ❌ Create custom components when PrimeReact exists
|
|
736
|
+
|
|
737
|
+
**Remember:** Layout changes structure. Design changes appearance. Keep them separate.
|