@rafters/shared 0.2.3

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/src/types.ts ADDED
@@ -0,0 +1,651 @@
1
+ /**
2
+ * @rafters/shared - W3C DTCG Types
3
+ * Core type definitions for Rafters design system following W3C Design Token Community Group spec
4
+ */
5
+
6
+ import { z } from 'zod';
7
+
8
+ /**
9
+ * Signal to clear an override and revert to computed value.
10
+ * Use with registry.set(tokenName, COMPUTED) to let the system decide.
11
+ */
12
+ declare const computedBrand: unique symbol;
13
+ export const COMPUTED: typeof computedBrand = Symbol.for(
14
+ 'rafters.computed',
15
+ ) as typeof computedBrand;
16
+ export type ComputedSymbol = typeof COMPUTED;
17
+
18
+ /**
19
+ * OKLCH Color Space
20
+ * Perceptually uniform color space based on human vision
21
+ * https://www.w3.org/TR/css-color-4/#ok-lab
22
+ */
23
+ export const OKLCHSchema = z.object({
24
+ /** Lightness: 0 (black) to 1 (white) */
25
+ l: z.number().min(0).max(1),
26
+ /** Chroma: 0 (gray) to ~0.4 (vivid) - no theoretical max */
27
+ c: z.number().min(0),
28
+ /** Hue: 0-360 degrees (red=0, yellow=90, green=150, cyan=180, blue=270, magenta=330) */
29
+ h: z.number().min(0).max(360),
30
+ /** Alpha transparency: 0 (transparent) to 1 (opaque) */
31
+ alpha: z.number().min(0).max(1).optional().default(1),
32
+ });
33
+
34
+ export type OKLCH = z.infer<typeof OKLCHSchema>;
35
+
36
+ /**
37
+ * W3C DTCG Token Base
38
+ * All design tokens follow this structure
39
+ * https://design-tokens.github.io/community-group/format/
40
+ */
41
+ export const DTCGTokenBaseSchema = z.object({
42
+ /** Required: The token's value */
43
+ $value: z.unknown(),
44
+ /** Token type - helps tools understand how to use the value */
45
+ $type: z
46
+ .enum([
47
+ 'color',
48
+ 'dimension',
49
+ 'fontFamily',
50
+ 'fontWeight',
51
+ 'duration',
52
+ 'cubicBezier',
53
+ 'number',
54
+ 'strokeStyle',
55
+ 'border',
56
+ 'transition',
57
+ 'shadow',
58
+ 'gradient',
59
+ 'typography',
60
+ ])
61
+ .optional(),
62
+ /** Human-readable description */
63
+ $description: z.string().optional(),
64
+ /** Deprecation marker - boolean or string explaining why */
65
+ $deprecated: z.union([z.boolean(), z.string()]).optional(),
66
+ /** Vendor-specific metadata */
67
+ $extensions: z.record(z.string(), z.unknown()).optional(),
68
+ });
69
+
70
+ export type DTCGTokenBase = z.infer<typeof DTCGTokenBaseSchema>;
71
+
72
+ /**
73
+ * W3C DTCG Color Value (OKLCH format)
74
+ */
75
+ export const DTCGColorValueSchema = z.object({
76
+ colorSpace: z.literal('oklch'),
77
+ channels: z.tuple([
78
+ z.number().min(0).max(1), // Lightness
79
+ z.number().min(0), // Chroma
80
+ z.number().min(0).max(360), // Hue
81
+ ]),
82
+ alpha: z.number().min(0).max(1).optional().default(1),
83
+ });
84
+
85
+ export type DTCGColorValue = z.infer<typeof DTCGColorValueSchema>;
86
+
87
+ /**
88
+ * W3C DTCG Color Token
89
+ */
90
+ export const DTCGColorTokenSchema = DTCGTokenBaseSchema.extend({
91
+ $value: z.union([
92
+ DTCGColorValueSchema, // Explicit OKLCH
93
+ z.string().regex(/^\{[^{}]+\}$/), // Reference: {color.primary.500}
94
+ ]),
95
+ $type: z.literal('color'),
96
+ });
97
+
98
+ export type DTCGColorToken = z.infer<typeof DTCGColorTokenSchema>;
99
+
100
+ /**
101
+ * W3C DTCG Group
102
+ * Groups organize tokens hierarchically
103
+ */
104
+ export type DTCGGroup = {
105
+ $type?: string | undefined;
106
+ $description?: string | undefined;
107
+ $deprecated?: boolean | string | undefined;
108
+ $extensions?: Record<string, unknown> | undefined;
109
+ [key: string]: DTCGTokenBase | DTCGGroup | string | boolean | unknown;
110
+ };
111
+
112
+ export const DTCGGroupSchema: z.ZodType<DTCGGroup> = z
113
+ .object({
114
+ $type: z.string().optional(),
115
+ $description: z.string().optional(),
116
+ $deprecated: z.union([z.boolean(), z.string()]).optional(),
117
+ $extensions: z.record(z.string(), z.unknown()).optional(),
118
+ })
119
+ .catchall(z.lazy(() => z.union([DTCGTokenBaseSchema, DTCGGroupSchema])));
120
+
121
+ /**
122
+ * Complete DTCG Design Token File
123
+ */
124
+ export const DTCGFileSchema = z.record(z.string(), z.union([DTCGTokenBaseSchema, DTCGGroupSchema]));
125
+
126
+ export type DTCGFile = z.infer<typeof DTCGFileSchema>;
127
+
128
+ /**
129
+ * Helper: Convert OKLCH to DTCG Color Value
130
+ */
131
+ export function oklchToDTCG(oklch: OKLCH): DTCGColorValue {
132
+ return {
133
+ colorSpace: 'oklch',
134
+ channels: [oklch.l, oklch.c, oklch.h],
135
+ alpha: oklch.alpha ?? 1,
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Helper: Convert DTCG Color Value to OKLCH
141
+ */
142
+ export function dtcgToOKLCH(dtcg: DTCGColorValue): OKLCH {
143
+ return {
144
+ l: dtcg.channels[0],
145
+ c: dtcg.channels[1],
146
+ h: dtcg.channels[2],
147
+ alpha: dtcg.alpha ?? 1,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Rafters Intelligence Types
153
+ * Extended schemas for AI-readable design intelligence
154
+ */
155
+
156
+ // Color Vision Types for accessibility
157
+ export const ColorVisionTypeSchema = z.enum([
158
+ 'normal',
159
+ 'deuteranopia', // Red-green (most common)
160
+ 'protanopia', // Red-green
161
+ 'tritanopia', // Blue-yellow (rare)
162
+ ]);
163
+
164
+ export type ColorVisionType = z.infer<typeof ColorVisionTypeSchema>;
165
+
166
+ // Accessibility Contrast Levels
167
+ export const ContrastLevelSchema = z.enum(['AA', 'AAA']);
168
+
169
+ export type ContrastLevel = z.infer<typeof ContrastLevelSchema>;
170
+
171
+ // Component Intelligence for AI consumption
172
+ export const ComponentIntelligenceSchema = z.object({
173
+ cognitiveLoad: z.number().min(1).max(5), // 1=simple, 5=complex
174
+ attentionHierarchy: z.string(), // How this component fits in attention economy
175
+ safetyConstraints: z.string().optional(), // Safety patterns required (e.g., confirmation)
176
+ accessibilityRules: z.string(), // WCAG compliance requirements
177
+ usageContext: z.string(), // When/where to use this component
178
+ decisionConstraints: z.string().optional(), // AI decision-making constraints
179
+ });
180
+
181
+ export type ComponentIntelligence = z.infer<typeof ComponentIntelligenceSchema>;
182
+
183
+ // CVA (class-variance-authority) intelligence schemas
184
+ export const ClassMappingSchema = z.object({
185
+ propName: z.string(),
186
+ values: z.record(z.string(), z.array(z.string())),
187
+ });
188
+
189
+ export const CVAIntelligenceSchema = z
190
+ .object({
191
+ baseClasses: z.array(z.string()),
192
+ propMappings: z.array(ClassMappingSchema),
193
+ allClasses: z.array(z.string()),
194
+ css: z.string().optional(), // Generated critical CSS for preview component
195
+ })
196
+ .optional();
197
+
198
+ export type ClassMapping = z.infer<typeof ClassMappingSchema>;
199
+ export type CVAIntelligence = z.infer<typeof CVAIntelligenceSchema>;
200
+
201
+ // CVA structure for preview rendering (without optional css field)
202
+ export const PreviewCVASchema = z.object({
203
+ baseClasses: z.array(z.string()),
204
+ propMappings: z.array(ClassMappingSchema),
205
+ allClasses: z.array(z.string()),
206
+ });
207
+
208
+ export type PreviewCVA = z.infer<typeof PreviewCVASchema>;
209
+
210
+ // Rafters intelligence schemas for CLI compatibility
211
+ export const IntelligenceSchema = z.object({
212
+ cognitiveLoad: z.number().min(0).max(10),
213
+ attentionEconomics: z.string(),
214
+ accessibility: z.string(),
215
+ trustBuilding: z.string(),
216
+ semanticMeaning: z.string(),
217
+ cva: CVAIntelligenceSchema,
218
+ });
219
+
220
+ export const UsagePatternsSchema = z.object({
221
+ dos: z.array(z.string()),
222
+ nevers: z.array(z.string()),
223
+ });
224
+
225
+ export const DesignGuideSchema = z.object({
226
+ name: z.string(),
227
+ url: z.string(),
228
+ });
229
+
230
+ export const ExampleSchema = z.object({
231
+ title: z.string().optional(),
232
+ code: z.string(),
233
+ description: z.string().optional(),
234
+ });
235
+
236
+ export type UsagePatterns = z.infer<typeof UsagePatternsSchema>;
237
+ export type DesignGuide = z.infer<typeof DesignGuideSchema>;
238
+ export type Example = z.infer<typeof ExampleSchema>;
239
+ export type Intelligence = z.infer<typeof IntelligenceSchema>;
240
+
241
+ // Color Intelligence Schema (from API with uncertainty quantification)
242
+ // Note: Color naming is now deterministic via generateColorName() in @rafters/color-utils
243
+ export const ColorIntelligenceSchema = z.object({
244
+ // Designer-facing evocative handle ("Aged Terracotta", "Solar Citrine") from
245
+ // the intelligence generator. Language, not identity: the deterministic
246
+ // `name` on ColorValue is the stable, parseable address; `label` is the
247
+ // voice. Optional because pre-label intelligence persists in Vectorize and
248
+ // token files. Backported from platform's generateColorIntelligence, which
249
+ // requires it on generation.
250
+ label: z.string().optional(),
251
+ reasoning: z.string(),
252
+ emotionalImpact: z.string(),
253
+ culturalContext: z.string(),
254
+ accessibilityNotes: z.string(),
255
+ usageGuidance: z.string(),
256
+ balancingGuidance: z.string().optional(), // Based on perceptual weight
257
+ metadata: z
258
+ .object({
259
+ predictionId: z.string(),
260
+ confidence: z.number().min(0).max(1),
261
+ uncertaintyBounds: z.object({
262
+ lower: z.number().min(0).max(1),
263
+ upper: z.number().min(0).max(1),
264
+ confidenceInterval: z.number().min(0).max(1),
265
+ }),
266
+ qualityScore: z.number().min(0).max(1),
267
+ method: z.enum(['bootstrap', 'quantile', 'ensemble', 'bayesian', 'conformal']),
268
+ })
269
+ .optional(),
270
+ });
271
+
272
+ export type ColorIntelligence = z.infer<typeof ColorIntelligenceSchema>;
273
+
274
+ // Color Harmonies Schema (calculated by color-utils)
275
+ export const ColorHarmoniesSchema = z.object({
276
+ complementary: OKLCHSchema,
277
+ triadic: z.array(OKLCHSchema),
278
+ analogous: z.array(OKLCHSchema),
279
+ tetradic: z.array(OKLCHSchema),
280
+ splitComplementary: z.array(OKLCHSchema),
281
+ monochromatic: z.array(OKLCHSchema),
282
+ });
283
+
284
+ export type ColorHarmonies = z.infer<typeof ColorHarmoniesSchema>;
285
+
286
+ // Color Accessibility Schema (calculated by color-utils)
287
+ export const ColorAccessibilitySchema = z.object({
288
+ // Pre-computed contrast matrices (indices into scale array)
289
+ wcagAA: z
290
+ .object({
291
+ normal: z.array(z.array(z.number())), // [[0, 5], [0, 6], ...] - pairs that meet AA
292
+ large: z.array(z.array(z.number())), // [[0, 4], [0, 5], ...] - more pairs for large text
293
+ })
294
+ .optional(),
295
+ wcagAAA: z
296
+ .object({
297
+ normal: z.array(z.array(z.number())), // [[0, 7], [0, 8], ...] - fewer pairs meet AAA
298
+ large: z.array(z.array(z.number())), // [[0, 6], [0, 7], ...]
299
+ })
300
+ .optional(),
301
+
302
+ // Basic compatibility data for the base color
303
+ onWhite: z.object({
304
+ wcagAA: z.boolean(),
305
+ wcagAAA: z.boolean(),
306
+ contrastRatio: z.number(),
307
+ // Pre-computed indices for scale lookups
308
+ aa: z.array(z.number()).optional(), // [5, 6, 7, 8, 9] - shades that pass AA on white
309
+ aaa: z.array(z.number()).optional(), // [7, 8, 9] - shades that pass AAA on white
310
+ }),
311
+ onBlack: z.object({
312
+ wcagAA: z.boolean(),
313
+ wcagAAA: z.boolean(),
314
+ contrastRatio: z.number(),
315
+ // Pre-computed indices for scale lookups
316
+ aa: z.array(z.number()).optional(), // [0, 1, 2, 3, 4] - shades that pass AA on black
317
+ aaa: z.array(z.number()).optional(), // [0, 1, 2] - shades that pass AAA on black
318
+ }),
319
+
320
+ // APCA (Advanced Perceptual Contrast Algorithm) scores - Future WCAG 3.0
321
+ apca: z
322
+ .object({
323
+ onWhite: z.number(), // APCA contrast score on white background
324
+ onBlack: z.number(), // APCA contrast score on black background
325
+ minFontSize: z.number(), // Minimum font size in px from fontLookupAPCA
326
+ })
327
+ .optional(),
328
+
329
+ // Color Vision Deficiency simulations
330
+ cvd: z
331
+ .object({
332
+ deuteranopia: OKLCHSchema, // Red-green (most common) simulated appearance
333
+ protanopia: OKLCHSchema, // Red-green simulated appearance
334
+ tritanopia: OKLCHSchema, // Blue-yellow (rare) simulated appearance
335
+ })
336
+ .optional(),
337
+ });
338
+
339
+ export type ColorAccessibility = z.infer<typeof ColorAccessibilitySchema>;
340
+
341
+ // Color Analysis Schema (calculated by color-utils)
342
+ export const ColorAnalysisSchema = z.object({
343
+ temperature: z.enum(['warm', 'cool', 'neutral']),
344
+ isLight: z.boolean(),
345
+ name: z.string(),
346
+ });
347
+
348
+ export type ColorAnalysis = z.infer<typeof ColorAnalysisSchema>;
349
+
350
+ // Atmospheric Weight Schema (atmospheric perspective color theory)
351
+ export const AtmosphericWeightSchema = z.object({
352
+ distanceWeight: z.number().min(0).max(1), // 0 = background, 1 = foreground
353
+ temperature: z.enum(['warm', 'neutral', 'cool']),
354
+ atmosphericRole: z.enum(['background', 'midground', 'foreground']),
355
+ });
356
+
357
+ export type AtmosphericWeight = z.infer<typeof AtmosphericWeightSchema>;
358
+
359
+ // Perceptual Weight Schema (visual balance in UI layouts)
360
+ export const PerceptualWeightSchema = z.object({
361
+ weight: z.number().min(0).max(1), // 0-1, higher = more visual weight
362
+ density: z.enum(['light', 'medium', 'heavy']),
363
+ balancingRecommendation: z.string(),
364
+ });
365
+
366
+ export type PerceptualWeight = z.infer<typeof PerceptualWeightSchema>;
367
+
368
+ // Status roles for semantic color suggestions. 'destructive' is canonical
369
+ // (matches colorWheel and the exporter's token vocabulary). Persisted files
370
+ // and the API may still carry the legacy 'danger' key; the schema lifts it.
371
+ export const SEMANTIC_STATUS_ROLES = ['destructive', 'success', 'warning', 'info'] as const;
372
+ export type SemanticStatusRole = (typeof SEMANTIC_STATUS_ROLES)[number];
373
+
374
+ // Semantic Color Suggestions Schema.
375
+ // 'danger' is a deprecated mirror of 'destructive', kept while apps/api (which
376
+ // asserts .danger) migrates to platform. Inputs with only the legacy key parse
377
+ // cleanly; outputs carry both until the mirror is removed.
378
+ export const SemanticColorSuggestionsSchema = z.preprocess(
379
+ (raw) => {
380
+ if (raw && typeof raw === 'object' && !('destructive' in raw) && 'danger' in raw) {
381
+ return { ...raw, destructive: (raw as { danger: unknown }).danger };
382
+ }
383
+ return raw;
384
+ },
385
+ z.object({
386
+ destructive: z.array(OKLCHSchema),
387
+ success: z.array(OKLCHSchema),
388
+ warning: z.array(OKLCHSchema),
389
+ info: z.array(OKLCHSchema),
390
+ /** @deprecated mirror of `destructive`; dies when apps/api leaves the repo */
391
+ danger: z.array(OKLCHSchema).optional(),
392
+ }),
393
+ );
394
+
395
+ export type SemanticColorSuggestions = z.infer<typeof SemanticColorSuggestionsSchema>;
396
+
397
+ // Color Value Schema for complex color structures
398
+ export const ColorValueSchema = z.object({
399
+ name: z.string(), // the fancy name from color-utils, IE ocean-blue
400
+ scale: z.array(OKLCHSchema), // OKLCH values array [50, 100, 200...900] positions - index maps to standard scale
401
+ token: z.string().optional(), // the semantic assignment IE, primary
402
+ value: z.string().optional(), // the string of the position in the scale IE 400
403
+ use: z.string().optional(), // any reasons the human notes for the color choice and assignment
404
+ states: z.record(z.string(), z.string()).optional(), // { hover: "blue-900", focus: "blue-700", ... }
405
+
406
+ // Complete intelligence data (from /api/color-intel)
407
+ intelligence: ColorIntelligenceSchema.optional(),
408
+ harmonies: ColorHarmoniesSchema.optional(),
409
+ accessibility: ColorAccessibilitySchema.optional(),
410
+ analysis: ColorAnalysisSchema.optional(),
411
+
412
+ // Advanced color theory intelligence (calculated by color-utils)
413
+ atmosphericWeight: AtmosphericWeightSchema.optional(),
414
+ perceptualWeight: PerceptualWeightSchema.optional(),
415
+ semanticSuggestions: SemanticColorSuggestionsSchema.optional(),
416
+
417
+ // Unique token ID for quick color lookups (e.g., "color-0.500-0.120-240.0")
418
+ tokenId: z.string().optional(),
419
+ });
420
+
421
+ export type ColorValue = z.infer<typeof ColorValueSchema>;
422
+
423
+ // Color Reference Schema for semantic tokens that reference color families
424
+ export const ColorReferenceSchema = z.object({
425
+ family: z.string(), // "flipped-out-gray", "ocean-blue", etc.
426
+ position: z.string(), // "50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"
427
+ });
428
+
429
+ export type ColorReference = z.infer<typeof ColorReferenceSchema>;
430
+
431
+ // Progression system labels stamped on tokens to record which math system
432
+ // produced them. Used only by TokenSchema below; the math-utils package
433
+ // carries the actual ratio definitions in DEFAULT_RATIOS. This array will be
434
+ // reshaped when the design-tokens package is refactored.
435
+ export const PROGRESSION_SYSTEMS = [
436
+ 'linear',
437
+ 'golden',
438
+ 'major-third',
439
+ 'minor-third',
440
+ 'perfect-fourth',
441
+ 'perfect-fifth',
442
+ 'augmented-fourth',
443
+ 'major-second',
444
+ 'minor-second',
445
+ 'custom',
446
+ ] as const;
447
+
448
+ // Binding: typed reference to the plugin + input that produces a token's value.
449
+ // When present on a Token, the registry resolves the value via the named plugin
450
+ // instead of treating the token as a leaf. The plugin name is opaque to the
451
+ // schema; the registry resolves it against its plugin map.
452
+ export const BindingSchema = z.object({
453
+ plugin: z.string(),
454
+ input: z.unknown(),
455
+ });
456
+ export type Binding = z.infer<typeof BindingSchema>;
457
+
458
+ // Override provenance: WHO put the value there, as a machine-readable kind
459
+ // rather than a free-text convention on the reason string.
460
+ // baseline -- the generated system value, restated as an explicit override
461
+ // preset -- written by applying a Studio value-set
462
+ // designer -- a human decision; preset application must not clobber it
463
+ // Extension point: 'measured' / 'tuned' can join the enum later.
464
+ export const OVERRIDE_KINDS = ['baseline', 'preset', 'designer'] as const;
465
+ export const OverrideKindSchema = z.enum(OVERRIDE_KINDS);
466
+ export type OverrideKind = z.infer<typeof OverrideKindSchema>;
467
+
468
+ // Comprehensive Design Token Schema - Single Source of Truth
469
+ export const TokenSchema = z.object({
470
+ // Core token data
471
+ name: z.string(),
472
+ value: z.union([z.string(), ColorValueSchema, ColorReferenceSchema]), // String, ColorValue for families, or ColorReference for semantic
473
+ category: z.string(),
474
+ namespace: z.string(),
475
+ binding: BindingSchema.optional(), // Plugin binding for cascade-driven values
476
+
477
+ // Typography-specific properties
478
+ lineHeight: z.string().optional(),
479
+
480
+ // AI Intelligence (comprehensive)
481
+ semanticMeaning: z.string().optional(),
482
+ usageContext: z.array(z.string()).optional(),
483
+ trustLevel: z.enum(['low', 'medium', 'high', 'critical']).optional(),
484
+ cognitiveLoad: z.number().min(1).max(10).optional(),
485
+ accessibilityLevel: z.enum(['AA', 'AAA']).optional(),
486
+ consequence: z.enum(['reversible', 'significant', 'permanent', 'destructive']).optional(),
487
+
488
+ // Designer intent - captures WHY values deviate from computed defaults
489
+ // Git blame shows WHAT changed and WHEN; these fields explain the reasoning
490
+ appliesWhen: z.array(z.string()).optional(), // ["data-heavy interfaces", "dashboard contexts"]
491
+ usagePatterns: z
492
+ .object({
493
+ do: z.array(z.string()), // ["Use for primary actions", "Pair with muted backgrounds"]
494
+ never: z.array(z.string()), // ["Multiple primary buttons competing", "On busy backgrounds"]
495
+ })
496
+ .optional(),
497
+
498
+ // Human override tracking - enables undo and agent intelligence.
499
+ // Required on every token. Null = generated baseline (no designer override).
500
+ // Populated = designer has explicitly changed this value with documented intent.
501
+ // Null IS the signal that no override exists -- do not auto-fill with synthetic strings.
502
+ userOverride: z
503
+ .object({
504
+ // Value before override (for undo)
505
+ previousValue: z.union([z.string(), ColorValueSchema, ColorReferenceSchema]),
506
+ // Why was this overridden
507
+ reason: z.string(),
508
+ // Additional context (e.g. "Q1 marketing campaign", "accessibility audit")
509
+ context: z.string().optional(),
510
+ // Who attributed the value. The reason string is for humans; kind is what
511
+ // machines branch on (preset application skips kind === 'designer').
512
+ // Optional and never defaulted: absent means the provenance is unknown,
513
+ // which is the honest state of every override written before this field
514
+ // existed.
515
+ kind: OverrideKindSchema.optional(),
516
+ })
517
+ .nullable(),
518
+
519
+ // Computed value from generation rule (before any override)
520
+ // Stored so agents can see what the system WOULD produce vs what human chose
521
+ computedValue: z.union([z.string(), ColorValueSchema, ColorReferenceSchema]).optional(),
522
+
523
+ // Dependency tracking for automatic regeneration
524
+ dependsOn: z.array(z.string()).optional(), // Parent token(s) - empty = root token
525
+ generationRule: z.string().optional(), // How generated: "calc({base}*2)", "state:hover", etc
526
+ progressionSystem: z.enum(PROGRESSION_SYSTEMS).optional(), // Mathematical system used
527
+ scalePosition: z.number().optional(), // Position in color/spacing scale
528
+ mathRelationship: z.string().optional(), // Mathematical expression for calc() rules
529
+
530
+ // Responsive behavior (containerQueryAware is default true for modern tokens)
531
+ containerQueryAware: z.boolean().optional().default(true),
532
+ pointerTypeAware: z.boolean().optional(), // Adapts to fine/coarse pointer
533
+ reducedMotionAware: z.boolean().optional(), // Respects prefers-reduced-motion
534
+ viewportAware: z.boolean().optional(), // Should generate responsive variants
535
+
536
+ // Localization (MVP+1: RTL/LTR support, basic locale spacing)
537
+ textDirection: z.enum(['ltr', 'rtl', 'auto']).optional(),
538
+ localeAware: z.boolean().optional(), // Token varies by locale
539
+
540
+ // Component associations
541
+ applicableComponents: z.array(z.string()).optional(), // ["button", "input", "card"]
542
+ requiredForComponents: z.array(z.string()).optional(), // Critical dependencies
543
+
544
+ // Interaction patterns
545
+ interactionType: z.enum(['hover', 'focus', 'active', 'disabled', 'loading']).optional(),
546
+ animationSafe: z.boolean().optional(), // Safe for vestibular disorders
547
+ highContrastMode: z.string().optional(), // Value for high contrast mode
548
+
549
+ // Motion tokens (derived from spacing progression for cohesive feel)
550
+ motionIntent: z.enum(['enter', 'exit', 'emphasis', 'transition']).optional(),
551
+ easingCurve: z.tuple([z.number(), z.number(), z.number(), z.number()]).optional(), // cubicBezier [x1, y1, x2, y2]
552
+ easingName: z
553
+ .enum(['standard', 'enter', 'exit', 'linear', 'spring-smooth', 'spring-snappy'])
554
+ .optional(),
555
+ delayMs: z.number().optional(), // Delay before animation starts
556
+
557
+ // Keyframe tokens (CSS @keyframes content / animation step definitions)
558
+ keyframeName: z.string().optional(), // Name of the keyframe (e.g., "fade-in")
559
+
560
+ // Animation tokens (combine keyframe + duration + easing)
561
+ animationName: z.string().optional(), // Name of the animation (e.g., "fade-in")
562
+ animationDuration: z.string().optional(), // Duration (e.g., "150ms", "1s")
563
+ animationEasing: z.string().optional(), // Easing function CSS value
564
+ animationIterations: z.string().optional(), // Iteration count (e.g., "1", "infinite")
565
+
566
+ // Focus tokens (WCAG 2.2 compliance, derive from ring unless overridden)
567
+ focusRingWidth: z.string().optional(), // e.g., "2px", "3px"
568
+ focusRingColor: z.string().optional(), // Reference to ring token or override
569
+ focusRingOffset: z.string().optional(), // e.g., "2px"
570
+ focusRingStyle: z.enum(['solid', 'dashed', 'double']).optional(),
571
+
572
+ // Elevation (pairs depth with shadow, can be independent)
573
+ elevationLevel: z
574
+ .enum(['surface', 'raised', 'overlay', 'sticky', 'modal', 'popover', 'tooltip'])
575
+ .optional(),
576
+ shadowToken: z.string().optional(), // Reference to paired shadow token
577
+
578
+ // Export behavior
579
+ generateUtilityClass: z.boolean().optional(), // Should create @utility class
580
+ tailwindOverride: z.boolean().optional(), // Overrides default TW value
581
+ customPropertyOnly: z.boolean().optional(), // CSS var only, no utility
582
+
583
+ // Validation hints (for human Studio validation)
584
+ contrastRatio: z.number().optional(), // Pre-calculated contrast
585
+ touchTargetSize: z.number().optional(), // Pre-calculated size in px
586
+ motionDuration: z.number().optional(), // Duration in ms
587
+
588
+ // Design system relationships
589
+ pairedWith: z.array(z.string()).optional(), // Other tokens used together
590
+ conflictsWith: z.array(z.string()).optional(), // Tokens that shouldn't be used together
591
+
592
+ // Meta information
593
+ description: z.string().optional(),
594
+ deprecated: z.boolean().optional(),
595
+ version: z.string().optional(),
596
+ lastModified: z.string().optional(),
597
+ generatedAt: z.string().optional(), // ISO timestamp when token was generated
598
+ requiresConfirmation: z.boolean().optional(), // UI pattern requirement for destructive actions
599
+ });
600
+
601
+ export type Token = z.infer<typeof TokenSchema>;
602
+
603
+ // Extract TokenUsagePatterns type for convenience
604
+ export type TokenUsagePatterns = NonNullable<Token['usagePatterns']>;
605
+
606
+ // Legacy alias for backward compatibility
607
+ export const SemanticTokenSchema = TokenSchema;
608
+ export type SemanticToken = Token;
609
+
610
+ /**
611
+ * Typography Element Override Schema
612
+ * Stores per-element typography overrides with why-gate provenance.
613
+ * When a designer overrides a specific element's typography (e.g. H3 uses
614
+ * title-medium but needs font-thin font-sans), this captures the override.
615
+ */
616
+ export const TypographyElementOverrideSchema = z.object({
617
+ /** CSS element selector, e.g. 'h3' */
618
+ element: z.string().min(1),
619
+ /** Base role this element uses, e.g. 'title-medium' */
620
+ role: z.string().min(1),
621
+ /** Only the properties that differ from the role */
622
+ overrides: z.object({
623
+ fontFamily: z.string().optional(),
624
+ fontWeight: z.string().optional(),
625
+ fontSize: z.string().optional(),
626
+ lineHeight: z.string().optional(),
627
+ letterSpacing: z.string().optional(),
628
+ }),
629
+ /** Why-gate: reason for the override (required, non-empty) */
630
+ why: z.string().min(1, 'Why-gate required: provide a reason for this override'),
631
+ /** Who made the override */
632
+ who: z.string().min(1),
633
+ /** When the override was made (ISO timestamp) */
634
+ when: z.string(),
635
+ });
636
+
637
+ export type TypographyElementOverride = z.infer<typeof TypographyElementOverrideSchema>;
638
+
639
+ /**
640
+ * Namespace File Schema
641
+ * File format for .rafters/tokens/{namespace}.rafters.json files
642
+ */
643
+ export const NamespaceFileSchema = z.object({
644
+ $schema: z.string(),
645
+ namespace: z.string(),
646
+ version: z.string(),
647
+ generatedAt: z.string(),
648
+ tokens: z.array(TokenSchema),
649
+ });
650
+
651
+ export type NamespaceFile = z.infer<typeof NamespaceFileSchema>;
package/src/version.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The lockstep suite version: @rafters/shared, @rafters/color-utils,
3
+ * @rafters/math-utils, and the rafters CLI all ship at this number.
4
+ * Written by packages/cli/scripts/sync-version.ts from the CLI's own
5
+ * package.json version at build time -- do not edit by hand.
6
+ */
7
+ export const RAFTERS_VERSION = '0.2.3';