guardian-framework 0.1.22 → 0.1.24
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/cli.js +28 -2
- package/dist/exports.js +28 -2
- package/package.json +1 -1
- package/templates/languages/angular-patterns.md +38 -0
- package/templates/languages/nextjs-patterns.md +39 -0
- package/templates/pi/extensions/architect-lib/helpers.ts +2 -0
- package/templates/pi/preflight_report.json +1 -1
- package/templates/pi/scripts/ci/check_ddd_structure.sh +3 -1
- package/templates/pi/skills/agents/angular-codegen.md +36 -0
- package/templates/pi/skills/agents/nextjs-codegen.md +34 -0
- package/templates/pi/skills/angular-enterprise-codegen.md +410 -0
- package/templates/pi/skills/design-system-enterprise-codegen.md +347 -0
- package/templates/pi/skills/nextjs-enterprise-codegen.md +383 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-system-enterprise-codegen
|
|
3
|
+
description: Full reference for design system architecture with DDD + component-driven development. Covers design tokens, CSS architecture, component API design, theming, accessibility, and testing. Framework-agnostic (works with React, Angular, Vue). Use alongside framework-specific codegen skills.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Design System & CSS Architecture — DDD Patterns
|
|
7
|
+
|
|
8
|
+
> Canonical skill for building enterprise design systems using DDD principles.
|
|
9
|
+
> Framework-agnostic — works with Next.js, Angular, Vue, or any component framework.
|
|
10
|
+
> Use alongside `.pi/skills/agents/nextjs-codegen.md` or `.pi/skills/agents/angular-codegen.md`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Design System Structure (DDD Pattern)
|
|
15
|
+
|
|
16
|
+
A design system is itself a bounded context within the DDD module structure:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
shared/
|
|
20
|
+
ui/ # Design system module
|
|
21
|
+
domain/ # Component API contracts, design tokens
|
|
22
|
+
tokens/ # Design tokens (the "why")
|
|
23
|
+
colors.ts # Color palette as domain values
|
|
24
|
+
typography.ts # Type scale as domain values
|
|
25
|
+
spacing.ts # Spacing scale as domain values
|
|
26
|
+
types.ts # Shared component prop types
|
|
27
|
+
component-api.ts # Base component interfaces
|
|
28
|
+
application/ # Component composition, state logic
|
|
29
|
+
variant-engine.ts # Variant resolution (cva pattern)
|
|
30
|
+
theme-service.ts # Theme switching logic
|
|
31
|
+
component-registry.ts # Component registration for theming
|
|
32
|
+
infrastructure/ # CSS engine adapters
|
|
33
|
+
css-adapter.ts # CSS Modules / Tailwind bridge
|
|
34
|
+
scss-adapter.ts # SCSS variable injection
|
|
35
|
+
theme-adapter.ts # CSS custom properties injection
|
|
36
|
+
interfaces/ # Actual rendered components
|
|
37
|
+
button/
|
|
38
|
+
button.tsx # Component
|
|
39
|
+
button.module.css # Styles
|
|
40
|
+
button.test.tsx # Tests
|
|
41
|
+
button.stories.tsx # Storybook stories
|
|
42
|
+
card/
|
|
43
|
+
modal/
|
|
44
|
+
form/
|
|
45
|
+
input/
|
|
46
|
+
select/
|
|
47
|
+
checkbox/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Why DDD for a Design System?
|
|
51
|
+
|
|
52
|
+
| Concern | Without DDD | With DDD |
|
|
53
|
+
|---------|-------------|----------|
|
|
54
|
+
| Design tokens | Scattered as CSS variables | Typed domain values in `domain/tokens/` |
|
|
55
|
+
| Component API | Implicit, documented elsewhere | Explicit interfaces in `domain/types.ts` |
|
|
56
|
+
| Variant logic | Inline in component | Shared `application/variant-engine.ts` |
|
|
57
|
+
| CSS framework | Tightly coupled | Adaptable via `infrastructure/css-adapter.ts` |
|
|
58
|
+
| Theming | Hardcoded colors | Injected through `infrastructure/theme-adapter.ts` |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 2. Design Tokens as Domain Values
|
|
63
|
+
|
|
64
|
+
Design tokens are domain value objects — they define the raw materials of your UI.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// shared/ui/domain/tokens/colors.ts
|
|
68
|
+
export class ColorToken {
|
|
69
|
+
private constructor(
|
|
70
|
+
public readonly name: string,
|
|
71
|
+
public readonly value: string,
|
|
72
|
+
public readonly description: string,
|
|
73
|
+
) {}
|
|
74
|
+
|
|
75
|
+
static readonly PRIMARY = new ColorToken('primary', '#0f766e', 'Primary brand color');
|
|
76
|
+
static readonly PRIMARY_HOVER = new ColorToken('primary-hover', '#0d5e56', 'Primary hover state');
|
|
77
|
+
static readonly DESTRUCTIVE = new ColorToken('destructive', '#dc2626', 'Destructive actions');
|
|
78
|
+
static readonly BACKGROUND = new ColorToken('background', '#ffffff', 'Page background');
|
|
79
|
+
static readonly FOREGROUND = new ColorToken('foreground', '#0f172a', 'Default text color');
|
|
80
|
+
|
|
81
|
+
static all(): ColorToken[] {
|
|
82
|
+
return [this.PRIMARY, this.PRIMARY_HOVER, this.DESTRUCTIVE, this.BACKGROUND, this.FOREGROUND];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// shared/ui/domain/tokens/spacing.ts
|
|
87
|
+
export class SpacingToken {
|
|
88
|
+
private constructor(
|
|
89
|
+
public readonly name: string,
|
|
90
|
+
public readonly value: string, // e.g. '0.25rem'
|
|
91
|
+
public readonly px: number, // e.g. 4
|
|
92
|
+
) {}
|
|
93
|
+
|
|
94
|
+
static readonly XS = new SpacingToken('xs', '0.25rem', 4);
|
|
95
|
+
static readonly SM = new SpacingToken('sm', '0.5rem', 8);
|
|
96
|
+
static readonly MD = new SpacingToken('md', '1rem', 16);
|
|
97
|
+
static readonly LG = new SpacingToken('lg', '1.5rem', 24);
|
|
98
|
+
static readonly XL = new SpacingToken('xl', '2rem', 32);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Component API Contracts
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// shared/ui/domain/component-api.ts
|
|
106
|
+
export interface ComponentVariant {
|
|
107
|
+
name: string;
|
|
108
|
+
description: string;
|
|
109
|
+
properties: Record<string, string>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ComponentAPI {
|
|
113
|
+
name: string;
|
|
114
|
+
description: string;
|
|
115
|
+
props: ComponentProp[];
|
|
116
|
+
variants: ComponentVariant[];
|
|
117
|
+
accessibility: AccessibilityGuide;
|
|
118
|
+
examples: CodeExample[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface ComponentProp {
|
|
122
|
+
name: string;
|
|
123
|
+
type: string;
|
|
124
|
+
required: boolean;
|
|
125
|
+
default?: unknown;
|
|
126
|
+
description: string;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 3. Variant Engine (Application Layer)
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// shared/ui/application/variant-engine.ts
|
|
136
|
+
export type VariantConfig = Record<string, Record<string, string>>;
|
|
137
|
+
|
|
138
|
+
export class VariantEngine {
|
|
139
|
+
constructor(private config: VariantConfig) {}
|
|
140
|
+
|
|
141
|
+
resolve(variant: string, size: string): string {
|
|
142
|
+
const variantClasses = this.config.variants?.[variant] ?? '';
|
|
143
|
+
const sizeClasses = this.config.sizes?.[size] ?? '';
|
|
144
|
+
return [variantClasses, sizeClasses].filter(Boolean).join(' ');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static cva(base: string, config: {
|
|
148
|
+
variants?: Record<string, Record<string, string>>;
|
|
149
|
+
sizes?: Record<string, string>;
|
|
150
|
+
defaults?: { variant?: string; size?: string };
|
|
151
|
+
}): (props: Record<string, string>) => string {
|
|
152
|
+
const engine = new VariantEngine(config);
|
|
153
|
+
return (props) => {
|
|
154
|
+
const v = props.variant ?? config.defaults?.variant ?? 'primary';
|
|
155
|
+
const s = props.size ?? config.defaults?.size ?? 'md';
|
|
156
|
+
return [base, engine.resolve(v, s)].filter(Boolean).join(' ');
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 4. CSS Architecture
|
|
165
|
+
|
|
166
|
+
### Layer Stack
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
1. CSS Reset / Normalize (global)
|
|
170
|
+
2. Design Tokens (custom props) (global)
|
|
171
|
+
3. Utility classes (Tailwind) (global)
|
|
172
|
+
4. Component styles (Modules) (scoped)
|
|
173
|
+
5. Page-specific styles (scoped)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Output: CSS Custom Properties
|
|
177
|
+
|
|
178
|
+
```scss
|
|
179
|
+
// infrastructure/_tokens.scss — Generated from domain/tokens/
|
|
180
|
+
:root {
|
|
181
|
+
--color-primary: #0f766e;
|
|
182
|
+
--color-primary-hover: #0d5e56;
|
|
183
|
+
--color-destructive: #dc2626;
|
|
184
|
+
--color-background: #ffffff;
|
|
185
|
+
--color-foreground: #0f172a;
|
|
186
|
+
--spacing-xs: 0.25rem;
|
|
187
|
+
--spacing-sm: 0.5rem;
|
|
188
|
+
--spacing-md: 1rem;
|
|
189
|
+
--radius-sm: 4px;
|
|
190
|
+
--radius-md: 8px;
|
|
191
|
+
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
|
|
192
|
+
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
|
|
193
|
+
--font-sans: 'Inter', system-ui, sans-serif;
|
|
194
|
+
--font-mono: 'JetBrains Mono', monospace;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
[data-theme='dark'] {
|
|
198
|
+
--color-background: #0f172a;
|
|
199
|
+
--color-foreground: #f8fafc;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Component Style Pattern
|
|
204
|
+
|
|
205
|
+
```scss
|
|
206
|
+
// interfaces/button/button.module.scss
|
|
207
|
+
@use '../../infrastructure/tokens' as *;
|
|
208
|
+
|
|
209
|
+
.button {
|
|
210
|
+
display: inline-flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
justify-content: center;
|
|
213
|
+
border-radius: var(--radius-md); // Reference tokens via CSS vars
|
|
214
|
+
font-family: var(--font-sans);
|
|
215
|
+
font-weight: 500;
|
|
216
|
+
transition: all 0.2s;
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
|
|
219
|
+
&:focus-visible {
|
|
220
|
+
outline: 2px solid var(--color-primary);
|
|
221
|
+
outline-offset: 2px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
&:disabled {
|
|
225
|
+
opacity: 0.5;
|
|
226
|
+
cursor: not-allowed;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.primary {
|
|
231
|
+
background: var(--color-primary);
|
|
232
|
+
color: white;
|
|
233
|
+
&:hover { background: var(--color-primary-hover); }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.secondary {
|
|
237
|
+
background: transparent;
|
|
238
|
+
border: 1px solid var(--color-primary);
|
|
239
|
+
color: var(--color-primary);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.sm { height: 2rem; padding: 0 var(--spacing-sm); font-size: 0.75rem; }
|
|
243
|
+
.md { height: 2.5rem; padding: 0 var(--spacing-md); }
|
|
244
|
+
.lg { height: 3rem; padding: 0 var(--spacing-lg); font-size: 1rem; }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### CSS Architecture Rules
|
|
248
|
+
- ✅ Use CSS Custom Properties for ALL design tokens — enables theming without recompilation
|
|
249
|
+
- ✅ Use CSS Modules for component-scoped styles — no leakage
|
|
250
|
+
- ✅ Use `:focus-visible` for keyboard focus — never `:focus` (which shows on click)
|
|
251
|
+
- ✅ Define breakpoints as CSS custom properties, not in JS
|
|
252
|
+
- ❌ No `!important` — ever. Restructure specificity instead
|
|
253
|
+
- ❌ No nested selectors deeper than 3 levels
|
|
254
|
+
- ❌ No color literals in component styles — always reference tokens
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 5. Accessibility (Built into Domain)
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// shared/ui/domain/component-api.ts
|
|
262
|
+
export interface AccessibilityGuide {
|
|
263
|
+
role: string;
|
|
264
|
+
aria: Record<string, string>;
|
|
265
|
+
keyboard: KeyboardInteraction[];
|
|
266
|
+
focusOrder: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface KeyboardInteraction {
|
|
270
|
+
key: string;
|
|
271
|
+
action: string;
|
|
272
|
+
description: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Button accessibility contract
|
|
276
|
+
export const BUTTON_ACCESSIBILITY: AccessibilityGuide = {
|
|
277
|
+
role: 'button',
|
|
278
|
+
aria: {
|
|
279
|
+
'aria-disabled': 'When button is disabled',
|
|
280
|
+
'aria-label': 'When icon-only button',
|
|
281
|
+
},
|
|
282
|
+
keyboard: [
|
|
283
|
+
{ key: 'Enter', action: 'activate', description: 'Activates the button' },
|
|
284
|
+
{ key: 'Space', action: 'activate', description: 'Activates the button' },
|
|
285
|
+
],
|
|
286
|
+
focusOrder: 1,
|
|
287
|
+
};
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 6. Testing the Design System
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// Domain token tests
|
|
296
|
+
describe('ColorToken', () => {
|
|
297
|
+
it('all tokens have unique names', () => {
|
|
298
|
+
const names = ColorToken.all().map(t => t.name);
|
|
299
|
+
expect(new Set(names).size).toBe(names.length);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Component tests
|
|
304
|
+
describe('Button', () => {
|
|
305
|
+
it('renders with variant classes', () => {
|
|
306
|
+
render(<Button variant="primary">Click</Button>);
|
|
307
|
+
expect(screen.getByRole('button')).toHaveClass('primary');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('is accessible when disabled', () => {
|
|
311
|
+
render(<Button disabled>Click</Button>);
|
|
312
|
+
expect(screen.getByRole('button')).toBeDisabled();
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Visual regression tests (Storybook + Chromatic)
|
|
317
|
+
// stories/button.stories.ts
|
|
318
|
+
export const Primary: Story = {
|
|
319
|
+
args: { variant: 'primary', children: 'Click me' },
|
|
320
|
+
};
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 7. Anti-Patterns — NEVER DO
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// ❌ Magic values
|
|
329
|
+
<Button color="#0f766e"> // BAD — use semantic tokens
|
|
330
|
+
|
|
331
|
+
// ❌ Framework-locked design system
|
|
332
|
+
import { ThemeProvider } from 'next-themes'; // BAD — use CSS custom properties
|
|
333
|
+
|
|
334
|
+
// ❌ Inline styles for layout
|
|
335
|
+
<div style={{ marginTop: 16 }}> // BAD — use spacing tokens
|
|
336
|
+
|
|
337
|
+
// ❌ Component with implicit API
|
|
338
|
+
<Button primary large /> // BAD — use explicit variant/size props
|
|
339
|
+
|
|
340
|
+
// ❌ Global CSS for one-off components
|
|
341
|
+
.hero-section { background: blue; } // BAD — use tokens + CSS Modules
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
*Version: 1.0.0*
|
|
347
|
+
*Last updated: 2026-07-03*
|