chaincss 2.1.39 → 2.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.
Files changed (33) hide show
  1. package/dist/compiler/accessibility-engine.d.ts +57 -0
  2. package/dist/compiler/constraint-solver.d.ts +85 -0
  3. package/dist/compiler/css-if-transpiler.d.ts +33 -0
  4. package/dist/compiler/design-orchestrator.d.ts +119 -0
  5. package/dist/compiler/intent-api.d.ts +73 -0
  6. package/dist/compiler/intent-engine.d.ts +19 -1
  7. package/dist/compiler/layout-intelligence.d.ts +71 -0
  8. package/dist/compiler/pass-manager.d.ts +157 -0
  9. package/dist/compiler/pattern-learner.d.ts +112 -0
  10. package/dist/compiler/responsive-inference.d.ts +63 -0
  11. package/dist/compiler/scroll-timeline.d.ts +91 -0
  12. package/dist/compiler/semantic-tokens.d.ts +57 -0
  13. package/dist/compiler/source-optimizer.d.ts +109 -0
  14. package/dist/compiler/style-ir.d.ts +183 -0
  15. package/dist/index.d.ts +23 -0
  16. package/dist/index.js +4126 -2
  17. package/package.json +1 -1
  18. package/src/compiler/accessibility-engine.ts +502 -0
  19. package/src/compiler/constraint-solver.ts +407 -0
  20. package/src/compiler/css-if-transpiler.ts +117 -0
  21. package/src/compiler/design-orchestrator.ts +322 -0
  22. package/src/compiler/intent-api.ts +505 -0
  23. package/src/compiler/intent-engine.ts +291 -1
  24. package/src/compiler/layout-intelligence.ts +697 -0
  25. package/src/compiler/pass-manager.ts +657 -0
  26. package/src/compiler/pattern-learner.ts +398 -0
  27. package/src/compiler/responsive-inference.ts +415 -0
  28. package/src/compiler/scroll-timeline.ts +284 -0
  29. package/src/compiler/semantic-tokens.ts +468 -0
  30. package/src/compiler/source-optimizer.ts +541 -0
  31. package/src/compiler/style-ir.ts +495 -0
  32. package/src/index.ts +209 -0
  33. package/ROADMAP.md +0 -31
@@ -0,0 +1,322 @@
1
+ // src/compiler/design-orchestrator.ts
2
+ /**
3
+ * Design System Orchestrator
4
+ *
5
+ * 1. WCAG Contrast Ratio Checker — validates text/background combos at build time
6
+ * 2. Contextual Tokens — tokens that auto-flip based on container context
7
+ * 3. Token Relationship Validator — ensures design tokens are consistent
8
+ */
9
+
10
+ // ============================================================================
11
+ // Types
12
+ // ============================================================================
13
+
14
+ export interface ContrastResult {
15
+ foreground: string;
16
+ background: string;
17
+ ratio: number;
18
+ passes: { AA: boolean; AALarge: boolean; AAA: boolean; AAALarge: boolean };
19
+ suggestion?: string;
20
+ }
21
+
22
+ export interface ContrastReport {
23
+ checks: ContrastResult[];
24
+ failures: ContrastResult[];
25
+ warnings: ContrastResult[];
26
+ passCount: number;
27
+ failCount: number;
28
+ summary: string;
29
+ }
30
+
31
+ export interface ContextualToken {
32
+ name: string;
33
+ default: string;
34
+ contexts: Record<string, string>; // e.g., { 'dark-section': 'white', 'light-section': 'black' }
35
+ }
36
+
37
+ export interface TokenContext {
38
+ name: string;
39
+ parentSelector?: string;
40
+ tokens: Record<string, any>;
41
+ }
42
+
43
+ // ============================================================================
44
+ // Color Utilities
45
+ // ============================================================================
46
+
47
+ /**
48
+ * Parse CSS color to RGBA components.
49
+ * Supports: hex, rgb(), rgba(), named colors
50
+ */
51
+ function parseColor(color: string): { r: number; g: number; b: number; a: number } | null {
52
+ const trimmed = color.trim().toLowerCase();
53
+
54
+ // hex
55
+ const hexMatch = trimmed.match(/^#([a-f0-9]{3}|[a-f0-9]{6}|[a-f0-9]{8})$/);
56
+ if (hexMatch) {
57
+ let hex = hexMatch[1];
58
+ if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
59
+ if (hex.length === 8) {
60
+ return {
61
+ r: parseInt(hex.slice(0, 2), 16),
62
+ g: parseInt(hex.slice(2, 4), 16),
63
+ b: parseInt(hex.slice(4, 6), 16),
64
+ a: parseInt(hex.slice(6, 8), 16) / 255,
65
+ };
66
+ }
67
+ return {
68
+ r: parseInt(hex.slice(0, 2), 16),
69
+ g: parseInt(hex.slice(2, 4), 16),
70
+ b: parseInt(hex.slice(4, 6), 16),
71
+ a: 1,
72
+ };
73
+ }
74
+
75
+ // rgb/rgba
76
+ const rgbMatch = trimmed.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/);
77
+ if (rgbMatch) {
78
+ return {
79
+ r: parseInt(rgbMatch[1]),
80
+ g: parseInt(rgbMatch[2]),
81
+ b: parseInt(rgbMatch[3]),
82
+ a: rgbMatch[4] ? parseFloat(rgbMatch[4]) : 1,
83
+ };
84
+ }
85
+
86
+ // Named colors (common subset)
87
+ const named: Record<string, [number, number, number]> = {
88
+ white: [255, 255, 255], black: [0, 0, 0],
89
+ red: [255, 0, 0], green: [0, 128, 0], blue: [0, 0, 255],
90
+ gray: [128, 128, 128], grey: [128, 128, 128],
91
+ transparent: [0, 0, 0],
92
+ };
93
+ if (named[trimmed]) {
94
+ const [r, g, b] = named[trimmed];
95
+ return { r, g, b, a: trimmed === 'transparent' ? 0 : 1 };
96
+ }
97
+
98
+ return null;
99
+ }
100
+
101
+ /**
102
+ * Calculate relative luminance per WCAG 2.1.
103
+ */
104
+ function relativeLuminance(r: number, g: number, b: number): number {
105
+ const rsrgb = r / 255;
106
+ const gsrgb = g / 255;
107
+ const bsrgb = b / 255;
108
+
109
+ const rLin = rsrgb <= 0.04045 ? rsrgb / 12.92 : Math.pow((rsrgb + 0.055) / 1.055, 2.4);
110
+ const gLin = gsrgb <= 0.04045 ? gsrgb / 12.92 : Math.pow((gsrgb + 0.055) / 1.055, 2.4);
111
+ const bLin = bsrgb <= 0.04045 ? bsrgb / 12.92 : Math.pow((bsrgb + 0.055) / 1.055, 2.4);
112
+
113
+ return 0.2126 * rLin + 0.7152 * gLin + 0.0722 * bLin;
114
+ }
115
+
116
+ /**
117
+ * Calculate WCAG contrast ratio between two colors.
118
+ * Returns value between 1 (no contrast) and 21 (max contrast).
119
+ */
120
+ export function contrastRatio(foreground: string, background: string): number {
121
+ const fg = parseColor(foreground);
122
+ const bg = parseColor(background);
123
+ if (!fg || !bg) return -1;
124
+
125
+ const lumFg = relativeLuminance(fg.r, fg.g, fg.b) + 0.05;
126
+ const lumBg = relativeLuminance(bg.r, bg.g, bg.b) + 0.05;
127
+
128
+ const lighter = Math.max(lumFg, lumBg);
129
+ const darker = Math.min(lumFg, lumBg);
130
+
131
+ return lighter / darker;
132
+ }
133
+
134
+ /**
135
+ * Check WCAG compliance levels.
136
+ * AA: 4.5:1 normal, 3:1 large text
137
+ * AAA: 7:1 normal, 4.5:1 large text
138
+ */
139
+ export function checkContrast(foreground: string, background: string): ContrastResult {
140
+ const ratio = contrastRatio(foreground, background);
141
+
142
+ return {
143
+ foreground,
144
+ background,
145
+ ratio: Math.round(ratio * 100) / 100,
146
+ passes: {
147
+ AA: ratio >= 4.5,
148
+ AALarge: ratio >= 3,
149
+ AAA: ratio >= 7,
150
+ AAALarge: ratio >= 4.5,
151
+ },
152
+ suggestion: ratio < 4.5
153
+ ? `Contrast ratio ${Math.round(ratio * 100) / 100} fails AA. Need ${Math.round((4.5 - ratio) * 100) / 100} more. Consider darkening/lightening.`
154
+ : undefined,
155
+ };
156
+ }
157
+
158
+ // ============================================================================
159
+ // Contrast Report Generator
160
+ // ============================================================================
161
+
162
+ /**
163
+ * Run contrast checks across a set of style definitions.
164
+ */
165
+ export function auditContrast(
166
+ styles: Array<{ selector: string; color: string; backgroundColor: string }>
167
+ ): ContrastReport {
168
+ const checks: ContrastResult[] = [];
169
+
170
+ for (const style of styles) {
171
+ if (style.color && style.backgroundColor) {
172
+ checks.push(checkContrast(style.color, style.backgroundColor));
173
+ }
174
+ }
175
+
176
+ const failures = checks.filter(c => !c.passes.AA);
177
+ const warnings = checks.filter(c => c.passes.AA && !c.passes.AAA);
178
+
179
+ return {
180
+ checks,
181
+ failures,
182
+ warnings,
183
+ passCount: checks.length - failures.length,
184
+ failCount: failures.length,
185
+ summary: failures.length === 0
186
+ ? 'All ' + checks.length + ' contrast checks pass AA.'
187
+ : failures.length + ' of ' + checks.length + ' contrast checks FAIL AA.',
188
+ };
189
+ }
190
+
191
+ // ============================================================================
192
+ // Contextual Token Engine
193
+ // ============================================================================
194
+
195
+ /**
196
+ * Contextual tokens that auto-resolve based on parent container.
197
+ *
198
+ * @example
199
+ * const buttonText = contextualToken({
200
+ * default: '#1a1a1a',
201
+ * contexts: {
202
+ * '.dark-section': '#ffffff',
203
+ * '.hero': '#ffffff',
204
+ * },
205
+ * });
206
+ *
207
+ * resolveContextual(buttonText, '.dark-section .my-button')
208
+ * // => '#ffffff'
209
+ */
210
+ export function createContextualToken(
211
+ defaultValue: string,
212
+ contexts: Record<string, string> = {}
213
+ ): ContextualToken {
214
+ const name = 'ctx-' + Math.random().toString(36).slice(2, 8);
215
+ return { name, default: defaultValue, contexts };
216
+ }
217
+
218
+ /**
219
+ * Resolve a contextual token based on the current selector path.
220
+ * Matches the most specific context that applies.
221
+ */
222
+ export function resolveContextual(
223
+ token: ContextualToken,
224
+ selectorPath: string
225
+ ): string {
226
+ // Find matching contexts — longest match wins (most specific)
227
+ let bestMatch = token.default;
228
+ let bestLength = 0;
229
+
230
+ for (const [context, value] of Object.entries(token.contexts)) {
231
+ if (selectorPath.includes(context) && context.length > bestLength) {
232
+ bestMatch = value;
233
+ bestLength = context.length;
234
+ }
235
+ }
236
+
237
+ return bestMatch;
238
+ }
239
+
240
+ /**
241
+ * Generate CSS custom property fallback for contextual tokens.
242
+ *
243
+ * @example
244
+ * generateContextualCSS('button-text', contextualToken)
245
+ * // => "
246
+ * // .my-button { --button-text: #1a1a1a; }
247
+ * // .dark-section .my-button { --button-text: #ffffff; }
248
+ * // "
249
+ */
250
+ export function generateContextualCSS(
251
+ propertyName: string,
252
+ token: ContextualToken,
253
+ baseSelector: string
254
+ ): string {
255
+ let css = '';
256
+
257
+ // Default
258
+ css += baseSelector + ' { ' + propertyName + ': ' + token.default + '; }\n';
259
+
260
+ // Context overrides
261
+ for (const [context, value] of Object.entries(token.contexts)) {
262
+ css += context + ' ' + baseSelector + ' { ' + propertyName + ': ' + value + '; }\n';
263
+ }
264
+
265
+ return css;
266
+ }
267
+
268
+ // ============================================================================
269
+ // Token Relationship Validator
270
+ // ============================================================================
271
+
272
+ /**
273
+ * Validate that token references are consistent.
274
+ * E.g., "primary" should have both foreground and background variants
275
+ * that contrast well with each other.
276
+ */
277
+ export function validateTokenRelationships(
278
+ tokens: Record<string, any>,
279
+ pairs: Array<{ foreground: string; background: string; label: string }>
280
+ ): ContrastReport {
281
+ const styles: Array<{ selector: string; color: string; backgroundColor: string }> = [];
282
+
283
+ for (const pair of pairs) {
284
+ const fg = resolveTokenPath(tokens, pair.foreground);
285
+ const bg = resolveTokenPath(tokens, pair.background);
286
+ if (fg && bg) {
287
+ styles.push({ selector: pair.label, color: fg, backgroundColor: bg });
288
+ }
289
+ }
290
+
291
+ return auditContrast(styles);
292
+ }
293
+
294
+ /**
295
+ * Resolve a dot-path token reference like "colors.primary.500".
296
+ */
297
+ function resolveTokenPath(tokens: Record<string, any>, path: string): string | null {
298
+ const parts = path.split('.');
299
+ let current: any = tokens;
300
+ for (const part of parts) {
301
+ if (current === undefined || current === null) return null;
302
+ current = current[part];
303
+ }
304
+ return typeof current === 'string' ? current : null;
305
+ }
306
+
307
+ // ============================================================================
308
+ // Quick API
309
+ // ============================================================================
310
+
311
+ export const orchestrator = {
312
+ contrastRatio,
313
+ checkContrast,
314
+ auditContrast,
315
+ createContextualToken,
316
+ resolveContextual,
317
+ generateContextualCSS,
318
+ validateTokenRelationships,
319
+ parseColor,
320
+ };
321
+
322
+ export default orchestrator;