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.
@@ -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*