guardian-framework 0.1.24 → 0.1.25

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 CHANGED
@@ -1335,7 +1335,7 @@ __export(exports_package, {
1335
1335
  bin: () => bin,
1336
1336
  author: () => author
1337
1337
  });
1338
- var name = "guardian-framework", version = "0.1.24", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1338
+ var name = "guardian-framework", version = "0.1.25", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1339
1339
  var init_package = __esm(() => {
1340
1340
  exports = {
1341
1341
  ".": {
package/dist/exports.js CHANGED
@@ -995,7 +995,7 @@ __export(exports_package, {
995
995
  bin: () => bin,
996
996
  author: () => author
997
997
  });
998
- var name = "guardian-framework", version = "0.1.24", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
998
+ var name = "guardian-framework", version = "0.1.25", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
999
999
  var init_package = __esm(() => {
1000
1000
  exports = {
1001
1001
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-03T07:45:52Z",
2
+ "timestamp": "2026-07-03T07:52:53Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: design-system-codegen
3
+ description: Minimal skill for design system architecture with DDD + adapter pattern. References full patterns on demand — never loads inline. Use when building design system components, tokens, or CSS integration.
4
+ model: inherit
5
+ tools: [Read, Grep]
6
+ ---
7
+
8
+ # Design System Code Generation — DDD + Adapter Pattern
9
+
10
+ > Full reference: `.pi/skills/design-system-enterprise-codegen.md`
11
+
12
+ ## Quick Reference
13
+
14
+ | When you need... | Read this section |
15
+ |-----------------|-------------------|
16
+ | Structure overview | Section 1 — DDD module layout, framework agnostic |
17
+ | Design tokens | Section 2 — ColorToken, SpacingToken, TypeScale as domain values |
18
+ | Variant engine | Section 3 — defineVariants(), type-safe component props |
19
+ | CSS adapter (Tailwind) | Section 4 — Tailwind class generation from tokens |
20
+ | CSS adapter (CSS Modules) | Section 4 — CSS variable generation |
21
+ | CSS adapter (MUI/Material) | Section 4 — MUI theme mapping |
22
+ | Component pattern | Section 5 — Button component using application layer |
23
+ | Theming (dark mode) | Section 6 — ThemeService, data-theme attribute |
24
+ | Testing | Section 7 — token tests, variant engine tests |
25
+ | Choosing your stack | Section 8 — Tailwind vs CSS Modules vs MUI by use case |
26
+
27
+ ## DDD Layer Contract for Design Systems
28
+
29
+ | Layer | Purpose | Example | CSS framework aware? |
30
+ |-------|---------|---------|---------------------|
31
+ | `domain/tokens/` | Typed design tokens | `ColorToken.PRIMARY` | No |
32
+ | `application/` | Variant resolution, theming | `defineVariants()` | No |
33
+ | `infrastructure/` | CSS adapter | `tailwind-adapter.ts` | **Yes** — this is the only layer |
34
+ | `interfaces/` | Rendered components | `button.tsx` | No (uses application layer) |
35
+
36
+ ## Rules
37
+ - NEVER read full reference — read specific sections
38
+ - Design tokens are pure TypeScript — zero CSS, zero framework imports
39
+ - Components import from `application/`, never from `infrastructure/` directly
40
+ - The adapter is the **only file** that changes when you switch CSS frameworks
41
+ - Pick one adapter per project — never mix Tailwind and MUI in the same component
@@ -1,94 +1,116 @@
1
1
  ---
2
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.
3
+ description: Full reference for design system architecture with DDD + adapter pattern. Covers design tokens, CSS framework integration (Tailwind/MUI/Material/CSS Modules), component API contracts, theming, accessibility, and testing. Framework-agnostic use with any component library.
4
4
  ---
5
5
 
6
- # Design System & CSS Architecture — DDD Patterns
6
+ # Design System & CSS Architecture — DDD with Adapter Pattern
7
7
 
8
8
  > Canonical skill for building enterprise design systems using DDD principles.
9
- > Framework-agnostic — works with Next.js, Angular, Vue, or any component framework.
9
+ > **Framework-agnostic** — works with Next.js, Angular, Vue, or any component framework.
10
+ > **CSS-agnostic** — works with Tailwind, MUI, Material, CSS Modules, or any styling approach.
11
+ >
10
12
  > Use alongside `.pi/skills/agents/nextjs-codegen.md` or `.pi/skills/agents/angular-codegen.md`.
11
13
 
12
14
  ---
13
15
 
14
- ## 1. Design System Structure (DDD Pattern)
16
+ ## The Core Idea: Domain Infrastructure Adapter
17
+
18
+ A design system shouldn't be coupled to a specific CSS framework. The DDD layer structure enforces this:
19
+
20
+ ```
21
+ domain/tokens/ ← Pure TypeScript: color values, spacing, typography
22
+ │ (zero CSS imports, zero framework imports)
23
+
24
+ application/ ← Logic: variant resolution, theme switching
25
+ │ (pure functions, no rendering)
26
+
27
+ infrastructure/ ← CSS ADAPTER: translates tokens into Tailwind/MUI/CSS Modules
28
+ │ (THIS is the only layer that knows about your CSS framework)
29
+
30
+ interfaces/button/ ← Components: use the adapter, render the UI
31
+ ```
15
32
 
16
- A design system is itself a bounded context within the DDD module structure:
33
+ | If you use... | Infrastructure adapter does | Components stay the same? |
34
+ |---------------|---------------------------|---------------------------|
35
+ | **Tailwind** | Generates `bg-primary text-white` class strings from tokens | ✅ Yes |
36
+ | **CSS Modules** | Generates `.module.css` with `var(--color-primary)` | ✅ Yes |
37
+ | **MUI** | Wraps MUI's `<Button>` component, maps tokens to MUI theme | ✅ Yes |
38
+ | **shadcn/ui** | Re-exports with token-based variants | ✅ Yes |
39
+ | **Styled Components** | Generates template literals | ✅ (but breaks RSC) |
40
+
41
+ ---
42
+
43
+ ## 1. Design System Structure (DDD Pattern)
17
44
 
18
45
  ```
19
46
  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
47
+ ui/
48
+ domain/ # Framework-free. Zero CSS imports.
49
+ tokens/ # Design tokens as typed value objects
50
+ colors.ts # ColorToken typed color palette
51
+ typography.ts # TypeScale font families, sizes
52
+ spacing.ts # SpacingScale margin, padding units
53
+ shadows.ts # ShadowToken elevation levels
54
+ breakpoints.ts # Breakpoint responsive breakpoints
55
+ types.ts # Component API contracts
56
+ component-api.ts # Base interfaces for all components
57
+ application/ # Logic layer (pure functions)
58
+ variant-engine.ts # Variant resolution (cva-like)
59
+ theme-service.ts # Dark/light theme switching
60
+ token-resolver.ts # Token value lookup
61
+ infrastructure/ # CSS ADAPTER — swap this per framework
62
+ tailwind-adapter.ts # Tailwind class generation from tokens
63
+ css-modules-adapter.ts # CSS Modules variable injection
64
+ mui-adapter.ts # MUI theme provider wrapper
65
+ material-adapter.ts # Angular Material theme mapping
66
+ interfaces/ # Rendered components
37
67
  button/
38
- button.tsx # Component
39
- button.module.css # Styles
68
+ button.tsx # Component (imports from application + infrastructure)
40
69
  button.test.tsx # Tests
41
70
  button.stories.tsx # Storybook stories
42
71
  card/
43
72
  modal/
44
73
  form/
45
- input/
46
- select/
47
- checkbox/
48
74
  ```
49
75
 
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
76
  ---
61
77
 
62
- ## 2. Design Tokens as Domain Values
78
+ ## 2. Domain Layer — Design Tokens (Framework-Free)
63
79
 
64
- Design tokens are domain value objects they define the raw materials of your UI.
80
+ This layer has zero CSS, zero framework, zero styling. It's pure TypeScript values.
65
81
 
66
82
  ```typescript
67
- // shared/ui/domain/tokens/colors.ts
83
+ // domain/tokens/colors.ts
68
84
  export class ColorToken {
69
85
  private constructor(
70
86
  public readonly name: string,
71
- public readonly value: string,
87
+ public readonly value: string, // Hex: '#0f766e'
72
88
  public readonly description: string,
73
89
  ) {}
74
90
 
75
91
  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');
92
+ static readonly PRIMARY_HOVER = new ColorToken('primary-hover', '#0d5e56');
93
+ static readonly DESTRUCTIVE = new ColorToken('destructive', '#dc2626');
94
+ static readonly BACKGROUND = new ColorToken('background', '#ffffff');
95
+ static readonly FOREGROUND = new ColorToken('foreground', '#0f172a');
96
+
97
+ // Dark mode overrides — still pure data, no CSS
98
+ static readonly DARK: Record<string, string> = {
99
+ background: '#0f172a',
100
+ foreground: '#f8fafc',
101
+ };
80
102
 
81
103
  static all(): ColorToken[] {
82
- return [this.PRIMARY, this.PRIMARY_HOVER, this.DESTRUCTIVE, this.BACKGROUND, this.FOREGROUND];
104
+ return Object.values(this).filter(v => v instanceof ColorToken);
83
105
  }
84
106
  }
85
107
 
86
- // shared/ui/domain/tokens/spacing.ts
108
+ // domain/tokens/spacing.ts
87
109
  export class SpacingToken {
88
- private constructor(
110
+ constructor(
89
111
  public readonly name: string,
90
- public readonly value: string, // e.g. '0.25rem'
91
- public readonly px: number, // e.g. 4
112
+ public readonly value: string, // CSS value: '0.25rem'
113
+ public readonly px: number, // Design value: 4
92
114
  ) {}
93
115
 
94
116
  static readonly XS = new SpacingToken('xs', '0.25rem', 4);
@@ -97,251 +119,233 @@ export class SpacingToken {
97
119
  static readonly LG = new SpacingToken('lg', '1.5rem', 24);
98
120
  static readonly XL = new SpacingToken('xl', '2rem', 32);
99
121
  }
100
- ```
101
122
 
102
- ### Component API Contracts
123
+ // domain/tokens/typography.ts
124
+ export class TypeScale {
125
+ constructor(
126
+ public readonly name: string,
127
+ public readonly fontSize: string,
128
+ public readonly lineHeight: string,
129
+ public readonly fontWeight: number,
130
+ ) {}
103
131
 
104
- ```typescript
105
- // shared/ui/domain/component-api.ts
106
- export interface ComponentVariant {
107
- name: string;
108
- description: string;
109
- properties: Record<string, string>;
132
+ static readonly H1 = new TypeScale('h1', '2.5rem', '1.2', 700);
133
+ static readonly H2 = new TypeScale('h2', '2rem', '1.3', 600);
134
+ static readonly BODY = new TypeScale('body', '1rem', '1.5', 400);
135
+ static readonly SMALL = new TypeScale('small', '0.875rem', '1.5', 400);
110
136
  }
137
+ ```
111
138
 
112
- export interface ComponentAPI {
113
- name: string;
114
- description: string;
115
- props: ComponentProp[];
116
- variants: ComponentVariant[];
117
- accessibility: AccessibilityGuide;
118
- examples: CodeExample[];
119
- }
139
+ ### Why tokens as domain objects?
120
140
 
121
- export interface ComponentProp {
122
- name: string;
123
- type: string;
124
- required: boolean;
125
- default?: unknown;
126
- description: string;
127
- }
128
- ```
141
+ - **Type-safe** `ColorToken.PRIMARY.value` is a `string`, can't pass an invalid color
142
+ - **Documented** — each token has a `description` field, self-documenting
143
+ - **Enumerable** — `ColorToken.all()` can generate CSS variables, Tailwind config, or MUI theme
144
+ - **Framework-independent** — the exact same tokens feed Tailwind, CSS Modules, or MUI
129
145
 
130
146
  ---
131
147
 
132
- ## 3. Variant Engine (Application Layer)
148
+ ## 3. Application Layer — Variant Engine
133
149
 
134
150
  ```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
- }
151
+ // application/variant-engine.ts
152
+ export type VariantDefinition = {
153
+ base: string;
154
+ variants: Record<string, Record<string, string>>;
155
+ defaults: Record<string, string>;
156
+ };
146
157
 
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
- }
158
+ export function defineVariants(config: VariantDefinition) {
159
+ return (props: Record<string, string>): string => {
160
+ const classes = [config.base];
161
+ for (const [key, values] of Object.entries(config.variants)) {
162
+ const value = props[key] ?? config.defaults[key];
163
+ if (value && values[value]) classes.push(values[value]);
164
+ }
165
+ return classes.filter(Boolean).join(' ');
166
+ };
159
167
  }
160
168
  ```
161
169
 
162
170
  ---
163
171
 
164
- ## 4. CSS Architecture
172
+ ## 4. Infrastructure — CSS Framework Adapters
165
173
 
166
- ### Layer Stack
174
+ ### Tailwind Adapter
167
175
 
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;
176
+ ```typescript
177
+ // infrastructure/tailwind-adapter.ts
178
+ // Translates domain tokens → Tailwind utility classes
179
+ import { ColorToken } from '../domain/tokens/colors';
180
+ import { SpacingToken } from '../domain/tokens/spacing';
181
+
182
+ export function tailwindColor(token: ColorToken): string {
183
+ const map: Record<string, string> = {
184
+ '#0f766e': 'bg-teal-700 text-white',
185
+ '#dc2626': 'bg-red-600 text-white',
186
+ '#ffffff': 'bg-white text-gray-900',
187
+ '#0f172a': 'bg-slate-900 text-white',
188
+ };
189
+ return map[token.value] ?? '';
195
190
  }
196
191
 
197
- [data-theme='dark'] {
198
- --color-background: #0f172a;
199
- --color-foreground: #f8fafc;
192
+ export function tailwindSpacing(token: SpacingToken): string {
193
+ const map: Record<number, string> = {
194
+ 4: 'p-1', 8: 'p-2', 16: 'p-4', 24: 'p-6', 32: 'p-8',
195
+ };
196
+ return map[token.px] ?? '';
200
197
  }
201
198
  ```
202
199
 
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
- }
200
+ ### CSS Modules Adapter
223
201
 
224
- &:disabled {
225
- opacity: 0.5;
226
- cursor: not-allowed;
227
- }
228
- }
202
+ ```typescript
203
+ // infrastructure/css-modules-adapter.ts
204
+ // Generates CSS custom properties from domain tokens
205
+ import { ColorToken } from '../domain/tokens/colors';
206
+ import { SpacingToken } from '../domain/tokens/spacing';
229
207
 
230
- .primary {
231
- background: var(--color-primary);
232
- color: white;
233
- &:hover { background: var(--color-primary-hover); }
234
- }
208
+ export function generateCSSVariables(): string {
209
+ const colors = ColorToken.all().map(t =>
210
+ ` --color-${t.name}: ${t.value};`
211
+ ).join('\n');
235
212
 
236
- .secondary {
237
- background: transparent;
238
- border: 1px solid var(--color-primary);
239
- color: var(--color-primary);
213
+ return `:root {\n${colors}\n}`;
240
214
  }
215
+ ```
216
+
217
+ ### MUI Adapter
241
218
 
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; }
219
+ ```typescript
220
+ // infrastructure/mui-adapter.ts
221
+ // Maps domain tokens to MUI theme structure
222
+ import { createTheme } from '@mui/material';
223
+ import { ColorToken } from '../domain/tokens/colors';
224
+
225
+ export function createMuiTheme() {
226
+ return createTheme({
227
+ palette: {
228
+ primary: { main: ColorToken.PRIMARY.value },
229
+ background: { default: ColorToken.BACKGROUND.value },
230
+ },
231
+ });
232
+ }
245
233
  ```
246
234
 
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
235
+ ### Choosing an Adapter
236
+
237
+ | Adapter | When to use | Tradeoffs |
238
+ |---------|-------------|-----------|
239
+ | **Tailwind** | Startups, rapid prototyping, utility-first | Class strings get long; needs `clsx`/`tailwind-merge` |
240
+ | **CSS Modules** | Enterprise, strict design systems, component libraries | More boilerplate; full control over CSS |
241
+ | **MUI** | Need a complete component library out of the box | Bundle size; customization can be complex |
242
+ | **Material (Angular)** | Angular projects needing Material Design | Framework-locked; heavy |
243
+ | **shadcn/ui** | Next.js projects wanting Radix primitives with Tailwind | Requires Tailwind; new ecosystem |
244
+
245
+ **Rule:** Pick one adapter per project. Never mix. The adapter is the only file that changes when you switch CSS frameworks.
255
246
 
256
247
  ---
257
248
 
258
- ## 5. Accessibility (Built into Domain)
249
+ ## 5. Component Implementation Pattern
259
250
 
260
251
  ```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
- }
252
+ // interfaces/button/button.tsx
253
+ import { defineVariants } from '../../application/variant-engine';
254
+ // ↑ import from application, not infrastructure
255
+ // Components DON'T import the adapter directly
256
+
257
+ const buttonVariants = defineVariants({
258
+ base: 'inline-flex items-center justify-center rounded-md transition-colors',
259
+ variants: {
260
+ variant: {
261
+ primary: 'bg-primary text-white hover:bg-primary-hover',
262
+ secondary: 'border border-primary text-primary hover:bg-primary/10',
263
+ ghost: 'hover:bg-accent',
264
+ },
265
+ size: { sm: 'h-9 px-3', md: 'h-10 px-4', lg: 'h-11 px-8' },
266
+ },
267
+ defaults: { variant: 'primary', size: 'md' },
268
+ });
268
269
 
269
- export interface KeyboardInteraction {
270
- key: string;
271
- action: string;
272
- description: string;
270
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
271
+ variant?: 'primary' | 'secondary' | 'ghost';
272
+ size?: 'sm' | 'md' | 'lg';
273
273
  }
274
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
- };
275
+ export function Button({ variant, size, className, ...props }: ButtonProps) {
276
+ return (
277
+ <button
278
+ className={[buttonVariants({ variant, size }), className].filter(Boolean).join(' ')}
279
+ {...props}
280
+ />
281
+ );
282
+ }
288
283
  ```
289
284
 
285
+ **Key rule:** Components import from `application/variant-engine.ts`, not from `infrastructure/`. The infrastructure adapter is only used at the app root (theme provider, CSS variable injection).
286
+
290
287
  ---
291
288
 
292
- ## 6. Testing the Design System
289
+ ## 6. Theming (Dark Mode)
293
290
 
294
291
  ```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
- });
292
+ // application/theme-service.ts
293
+ import { ColorToken } from '../domain/tokens/colors';
302
294
 
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
- });
295
+ export type Theme = 'light' | 'dark';
309
296
 
310
- it('is accessible when disabled', () => {
311
- render(<Button disabled>Click</Button>);
312
- expect(screen.getByRole('button')).toBeDisabled();
313
- });
314
- });
297
+ export class ThemeService {
298
+ private current: Theme = 'light';
315
299
 
316
- // Visual regression tests (Storybook + Chromatic)
317
- // stories/button.stories.ts
318
- export const Primary: Story = {
319
- args: { variant: 'primary', children: 'Click me' },
320
- };
300
+ get theme(): Theme { return this.current; }
301
+
302
+ setTheme(theme: Theme): void {
303
+ this.current = theme;
304
+ document.documentElement.setAttribute('data-theme', theme);
305
+ }
306
+
307
+ toggle(): void {
308
+ this.setTheme(this.current === 'light' ? 'dark' : 'light');
309
+ }
310
+ }
321
311
  ```
322
312
 
323
313
  ---
324
314
 
325
- ## 7. Anti-PatternsNEVER DO
315
+ ## 7. TestingFramework-Independent
326
316
 
327
317
  ```typescript
328
- // ❌ Magic values
329
- <Button color="#0f766e"> // BAD — use semantic tokens
318
+ // domain/tokens/colors.test.ts
319
+ describe('ColorToken', () => {
320
+ it('all tokens have valid hex values', () => {
321
+ for (const t of ColorToken.all()) {
322
+ expect(t.value).toMatch(/^#[0-9a-fA-F]{6}$/);
323
+ }
324
+ });
325
+ });
330
326
 
331
- // ❌ Framework-locked design system
332
- import { ThemeProvider } from 'next-themes'; // BAD use CSS custom properties
327
+ // application/variant-engine.test.ts
328
+ describe('VariantEngine', () => {
329
+ it('resolves variant classes', () => {
330
+ const button = defineVariants({ base: 'btn', variants: { color: { red: 'bg-red' } }, defaults: { color: 'red' } });
331
+ expect(button({})).toBe('btn bg-red');
332
+ });
333
+ });
334
+ ```
333
335
 
334
- // ❌ Inline styles for layout
335
- <div style={{ marginTop: 16 }}> // BAD — use spacing tokens
336
+ ---
336
337
 
337
- // Component with implicit API
338
- <Button primary large /> // BAD — use explicit variant/size props
338
+ ## 8. Choosing Your Stack — Quick Reference
339
339
 
340
- // Global CSS for one-off components
341
- .hero-section { background: blue; } // BAD — use tokens + CSS Modules
342
- ```
340
+ | Project type | Recommended adapter | Why |
341
+ |-------------|-------------------|-----|
342
+ | Next.js app, small team | Tailwind + shadcn/ui | Fastest path, good defaults, RSC-compatible |
343
+ | Next.js app, large team | CSS Modules + custom tokens | Full control, strict design system |
344
+ | Angular app, Material look | Angular Material adapter | Native integration, theming built-in |
345
+ | Angular app, custom design | CSS Modules + SCSS tokens | Full control, SCSS features |
346
+ | Multi-framework design system | CSS Modules + CSS Custom Properties | Works everywhere, zero framework lock-in |
343
347
 
344
348
  ---
345
349
 
346
- *Version: 1.0.0*
350
+ *Version: 1.1.0*
347
351
  *Last updated: 2026-07-03*
@@ -8,7 +8,7 @@ description: Full reference for Next.js enterprise code generation with DDD + Cl
8
8
  > Canonical skill for generating production-grade Next.js code with DDD patterns.
9
9
  > All code MUST follow these patterns. Validators enforce compliance.
10
10
  >
11
- > For design system and styling patterns, see: `.pi/skills/design-system-enterprise-codegen.md`
11
+ > For design system and styling patterns, see: `.pi/skills/design-system-enterprise-codegen.md` — tokens, CSS adapters, theming, component API contracts
12
12
 
13
13
  ---
14
14