@hale-bopp/valentino-engine 1.0.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 (144) hide show
  1. package/AGENTS.md +49 -0
  2. package/CHANGELOG.md +22 -0
  3. package/LICENSE +21 -0
  4. package/README.md +108 -0
  5. package/dist/bin/commands/audit.d.ts +2 -0
  6. package/dist/bin/commands/audit.d.ts.map +1 -0
  7. package/dist/bin/commands/audit.js +24 -0
  8. package/dist/bin/commands/audit.js.map +1 -0
  9. package/dist/bin/commands/catalog.d.ts +2 -0
  10. package/dist/bin/commands/catalog.d.ts.map +1 -0
  11. package/dist/bin/commands/catalog.js +21 -0
  12. package/dist/bin/commands/catalog.js.map +1 -0
  13. package/dist/bin/commands/contrast.d.ts +2 -0
  14. package/dist/bin/commands/contrast.d.ts.map +1 -0
  15. package/dist/bin/commands/contrast.js +22 -0
  16. package/dist/bin/commands/contrast.js.map +1 -0
  17. package/dist/bin/commands/guardrails.d.ts +2 -0
  18. package/dist/bin/commands/guardrails.d.ts.map +1 -0
  19. package/dist/bin/commands/guardrails.js +18 -0
  20. package/dist/bin/commands/guardrails.js.map +1 -0
  21. package/dist/bin/commands/manifest.d.ts +2 -0
  22. package/dist/bin/commands/manifest.d.ts.map +1 -0
  23. package/dist/bin/commands/manifest.js +21 -0
  24. package/dist/bin/commands/manifest.js.map +1 -0
  25. package/dist/bin/commands/probe.d.ts +2 -0
  26. package/dist/bin/commands/probe.d.ts.map +1 -0
  27. package/dist/bin/commands/probe.js +59 -0
  28. package/dist/bin/commands/probe.js.map +1 -0
  29. package/dist/bin/commands/validate.d.ts +2 -0
  30. package/dist/bin/commands/validate.d.ts.map +1 -0
  31. package/dist/bin/commands/validate.js +19 -0
  32. package/dist/bin/commands/validate.js.map +1 -0
  33. package/dist/bin/valentino.d.ts +7 -0
  34. package/dist/bin/valentino.d.ts.map +1 -0
  35. package/dist/bin/valentino.js +62 -0
  36. package/dist/bin/valentino.js.map +1 -0
  37. package/dist/core/catalog.d.ts +13 -0
  38. package/dist/core/catalog.d.ts.map +1 -0
  39. package/dist/core/catalog.js +63 -0
  40. package/dist/core/catalog.js.map +1 -0
  41. package/dist/core/catalog.test.d.ts +2 -0
  42. package/dist/core/catalog.test.d.ts.map +1 -0
  43. package/dist/core/catalog.test.js +115 -0
  44. package/dist/core/catalog.test.js.map +1 -0
  45. package/dist/core/contrast.d.ts +42 -0
  46. package/dist/core/contrast.d.ts.map +1 -0
  47. package/dist/core/contrast.js +98 -0
  48. package/dist/core/contrast.js.map +1 -0
  49. package/dist/core/contrast.test.d.ts +2 -0
  50. package/dist/core/contrast.test.d.ts.map +1 -0
  51. package/dist/core/contrast.test.js +82 -0
  52. package/dist/core/contrast.test.js.map +1 -0
  53. package/dist/core/css-named-colors.d.ts +7 -0
  54. package/dist/core/css-named-colors.d.ts.map +1 -0
  55. package/dist/core/css-named-colors.js +45 -0
  56. package/dist/core/css-named-colors.js.map +1 -0
  57. package/dist/core/guardrails.d.ts +16 -0
  58. package/dist/core/guardrails.d.ts.map +1 -0
  59. package/dist/core/guardrails.js +55 -0
  60. package/dist/core/guardrails.js.map +1 -0
  61. package/dist/core/guardrails.test.d.ts +2 -0
  62. package/dist/core/guardrails.test.d.ts.map +1 -0
  63. package/dist/core/guardrails.test.js +88 -0
  64. package/dist/core/guardrails.test.js.map +1 -0
  65. package/dist/core/hero-contract.d.ts +21 -0
  66. package/dist/core/hero-contract.d.ts.map +1 -0
  67. package/dist/core/hero-contract.js +70 -0
  68. package/dist/core/hero-contract.js.map +1 -0
  69. package/dist/core/hero-contract.test.d.ts +2 -0
  70. package/dist/core/hero-contract.test.d.ts.map +1 -0
  71. package/dist/core/hero-contract.test.js +71 -0
  72. package/dist/core/hero-contract.test.js.map +1 -0
  73. package/dist/core/manifest.d.ts +8 -0
  74. package/dist/core/manifest.d.ts.map +1 -0
  75. package/dist/core/manifest.js +17 -0
  76. package/dist/core/manifest.js.map +1 -0
  77. package/dist/core/manifest.test.d.ts +2 -0
  78. package/dist/core/manifest.test.d.ts.map +1 -0
  79. package/dist/core/manifest.test.js +45 -0
  80. package/dist/core/manifest.test.js.map +1 -0
  81. package/dist/core/page-spec.d.ts +14 -0
  82. package/dist/core/page-spec.d.ts.map +1 -0
  83. package/dist/core/page-spec.js +20 -0
  84. package/dist/core/page-spec.js.map +1 -0
  85. package/dist/core/page-spec.test.d.ts +2 -0
  86. package/dist/core/page-spec.test.d.ts.map +1 -0
  87. package/dist/core/page-spec.test.js +38 -0
  88. package/dist/core/page-spec.test.js.map +1 -0
  89. package/dist/core/presentation.d.ts +9 -0
  90. package/dist/core/presentation.d.ts.map +1 -0
  91. package/dist/core/presentation.js +65 -0
  92. package/dist/core/presentation.js.map +1 -0
  93. package/dist/core/presentation.test.d.ts +2 -0
  94. package/dist/core/presentation.test.d.ts.map +1 -0
  95. package/dist/core/presentation.test.js +63 -0
  96. package/dist/core/presentation.test.js.map +1 -0
  97. package/dist/core/rhythm.d.ts +25 -0
  98. package/dist/core/rhythm.d.ts.map +1 -0
  99. package/dist/core/rhythm.js +67 -0
  100. package/dist/core/rhythm.js.map +1 -0
  101. package/dist/core/rhythm.test.d.ts +2 -0
  102. package/dist/core/rhythm.test.d.ts.map +1 -0
  103. package/dist/core/rhythm.test.js +69 -0
  104. package/dist/core/rhythm.test.js.map +1 -0
  105. package/dist/core/section-integrity.d.ts +30 -0
  106. package/dist/core/section-integrity.d.ts.map +1 -0
  107. package/dist/core/section-integrity.js +72 -0
  108. package/dist/core/section-integrity.js.map +1 -0
  109. package/dist/core/section-integrity.test.d.ts +2 -0
  110. package/dist/core/section-integrity.test.d.ts.map +1 -0
  111. package/dist/core/section-integrity.test.js +83 -0
  112. package/dist/core/section-integrity.test.js.map +1 -0
  113. package/dist/core/types.d.ts +456 -0
  114. package/dist/core/types.d.ts.map +1 -0
  115. package/dist/core/types.js +7 -0
  116. package/dist/core/types.js.map +1 -0
  117. package/dist/index.d.ts +21 -0
  118. package/dist/index.d.ts.map +1 -0
  119. package/dist/index.js +23 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/mcp/index.d.ts +2 -0
  122. package/dist/mcp/index.d.ts.map +1 -0
  123. package/dist/mcp/index.js +126 -0
  124. package/dist/mcp/index.js.map +1 -0
  125. package/dist/skills/design-guidelines.d.ts +6 -0
  126. package/dist/skills/design-guidelines.d.ts.map +1 -0
  127. package/dist/skills/design-guidelines.js +11 -0
  128. package/dist/skills/design-guidelines.js.map +1 -0
  129. package/dist/skills/index.d.ts +7 -0
  130. package/dist/skills/index.d.ts.map +1 -0
  131. package/dist/skills/index.js +7 -0
  132. package/dist/skills/index.js.map +1 -0
  133. package/dist/skills/premium-design.d.ts +6 -0
  134. package/dist/skills/premium-design.d.ts.map +1 -0
  135. package/dist/skills/premium-design.js +11 -0
  136. package/dist/skills/premium-design.js.map +1 -0
  137. package/dist/skills/web-guardrails.d.ts +6 -0
  138. package/dist/skills/web-guardrails.d.ts.map +1 -0
  139. package/dist/skills/web-guardrails.js +11 -0
  140. package/dist/skills/web-guardrails.js.map +1 -0
  141. package/package.json +69 -0
  142. package/skills/valentino-premium-design.md +10 -0
  143. package/skills/valentino-web-guardrails.md +9 -0
  144. package/skills/web-design-guidelines.md +9 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Catalog Resolver — resolves PageSpec against a ValentinoCatalog.
3
+ * Pure functions, no fetch, no DOM. Consumer loads the catalog.
4
+ * Extracted from easyway-portal (src/utils/valentino-catalog.ts).
5
+ */
6
+ export function mergePresentation(presetPresentation, transitionPresentation, inlinePresentation) {
7
+ const merged = {
8
+ ...(presetPresentation || {}),
9
+ ...(transitionPresentation || {}),
10
+ ...(inlinePresentation || {}),
11
+ };
12
+ return Object.keys(merged).length > 0 ? merged : undefined;
13
+ }
14
+ export function isGovernanceAllowed(sectionType, pageProfile, governance) {
15
+ if (!governance)
16
+ return true;
17
+ if (governance.allowedPageProfiles?.length && (!pageProfile || !governance.allowedPageProfiles.includes(pageProfile))) {
18
+ return false;
19
+ }
20
+ if (governance.allowedSectionTypes?.length && !governance.allowedSectionTypes.includes(sectionType)) {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+ function resolveSectionWithCatalog(section, pageProfile, catalog) {
26
+ const presentation = section.presentation;
27
+ const presetId = presentation?.presetId;
28
+ const transitionProfileId = presentation?.transitionProfileId;
29
+ const presetEntry = presetId ? catalog.sectionPresets[presetId] : undefined;
30
+ const transitionEntry = transitionProfileId ? catalog.transitionProfiles[transitionProfileId] : undefined;
31
+ if (presetEntry && !isGovernanceAllowed(section.type, pageProfile, presetEntry.governance)) {
32
+ console.warn(`[ValentinoCatalog] Preset '${presetId}' is outside the approved perimeter for page profile '${pageProfile || 'n/a'}' and section '${section.type}'`);
33
+ }
34
+ if (transitionEntry && !isGovernanceAllowed(section.type, pageProfile, transitionEntry.governance)) {
35
+ console.warn(`[ValentinoCatalog] Transition profile '${transitionProfileId}' is outside the approved perimeter for page profile '${pageProfile || 'n/a'}' and section '${section.type}'`);
36
+ }
37
+ return {
38
+ ...section,
39
+ presentation: mergePresentation(presetEntry?.presentation, transitionEntry?.presentation, presentation),
40
+ };
41
+ }
42
+ export function resolvePageSpecWithCatalog(pageSpec, catalog) {
43
+ const blueprintEntry = pageSpec.blueprintId ? catalog.pageBlueprints[pageSpec.blueprintId] : undefined;
44
+ const blueprintSpec = blueprintEntry?.spec;
45
+ const templateId = pageSpec.templateId || blueprintSpec?.templateId;
46
+ const templateEntry = templateId ? catalog.templates[templateId] : undefined;
47
+ const resolvedProfile = pageSpec.profile || blueprintSpec?.profile || templateEntry?.page?.profile;
48
+ const resolvedSections = Array.isArray(pageSpec.sections) && pageSpec.sections.length > 0
49
+ ? pageSpec.sections
50
+ : (blueprintSpec?.sections || []);
51
+ return {
52
+ ...blueprintSpec,
53
+ ...pageSpec,
54
+ id: pageSpec.id,
55
+ templateId,
56
+ profile: resolvedProfile,
57
+ titleKey: pageSpec.titleKey || blueprintSpec?.titleKey,
58
+ themeId: pageSpec.themeId || blueprintSpec?.themeId || templateEntry?.page?.themeId,
59
+ chrome: pageSpec.chrome || blueprintSpec?.chrome || templateEntry?.page?.chrome,
60
+ sections: resolvedSections.map((section) => resolveSectionWithCatalog(section, resolvedProfile, catalog)),
61
+ };
62
+ }
63
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/core/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=catalog.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.test.d.ts","sourceRoot":"","sources":["../../src/core/catalog.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mergePresentation, isGovernanceAllowed, resolvePageSpecWithCatalog } from './catalog.js';
3
+ const emptyCatalog = {
4
+ version: '1',
5
+ templates: {},
6
+ sectionPresets: {},
7
+ transitionProfiles: {},
8
+ pageBlueprints: {},
9
+ };
10
+ describe('mergePresentation', () => {
11
+ it('returns undefined when all inputs are undefined', () => {
12
+ expect(mergePresentation(undefined, undefined, undefined)).toBeUndefined();
13
+ });
14
+ it('merges preset + inline', () => {
15
+ const result = mergePresentation({ surface: 'muted', tone: 'default' }, undefined, { tone: 'elevated' });
16
+ expect(result).toEqual({ surface: 'muted', tone: 'elevated' });
17
+ });
18
+ it('inline overrides transition overrides preset', () => {
19
+ const result = mergePresentation({ surface: 'muted' }, { surface: 'accent' }, { surface: 'dark' });
20
+ expect(result?.surface).toBe('dark');
21
+ });
22
+ });
23
+ describe('isGovernanceAllowed', () => {
24
+ it('allows everything when no governance', () => {
25
+ expect(isGovernanceAllowed('hero', 'home-signature')).toBe(true);
26
+ });
27
+ it('blocks section type not in allowedSectionTypes', () => {
28
+ expect(isGovernanceAllowed('hero', 'home-signature', {
29
+ allowedSectionTypes: ['cards', 'cta'],
30
+ })).toBe(false);
31
+ });
32
+ it('allows section type in allowedSectionTypes', () => {
33
+ expect(isGovernanceAllowed('cards', 'home-signature', {
34
+ allowedSectionTypes: ['cards', 'cta'],
35
+ })).toBe(true);
36
+ });
37
+ it('blocks page profile not in allowedPageProfiles', () => {
38
+ expect(isGovernanceAllowed('hero', 'conversion-form', {
39
+ allowedPageProfiles: ['home-signature'],
40
+ })).toBe(false);
41
+ });
42
+ it('allows page profile in allowedPageProfiles', () => {
43
+ expect(isGovernanceAllowed('hero', 'home-signature', {
44
+ allowedPageProfiles: ['home-signature'],
45
+ })).toBe(true);
46
+ });
47
+ it('blocks when page profile is undefined', () => {
48
+ expect(isGovernanceAllowed('hero', undefined, {
49
+ allowedPageProfiles: ['home-signature'],
50
+ })).toBe(false);
51
+ });
52
+ });
53
+ describe('resolvePageSpecWithCatalog', () => {
54
+ it('passes through a spec with no catalog refs', () => {
55
+ const spec = {
56
+ version: '1',
57
+ id: 'test',
58
+ sections: [{ type: 'hero', titleKey: 'h1' }],
59
+ };
60
+ const resolved = resolvePageSpecWithCatalog(spec, emptyCatalog);
61
+ expect(resolved.id).toBe('test');
62
+ expect(resolved.sections).toHaveLength(1);
63
+ });
64
+ it('resolves preset presentation', () => {
65
+ const catalog = {
66
+ ...emptyCatalog,
67
+ sectionPresets: {
68
+ 'hero-home': { presentation: { surface: 'shell-dark', tone: 'immersive' } },
69
+ },
70
+ };
71
+ const spec = {
72
+ version: '1',
73
+ id: 'test',
74
+ sections: [{
75
+ type: 'hero',
76
+ titleKey: 'h1',
77
+ presentation: { presetId: 'hero-home' },
78
+ }],
79
+ };
80
+ const resolved = resolvePageSpecWithCatalog(spec, catalog);
81
+ expect(resolved.sections[0].presentation?.surface).toBe('shell-dark');
82
+ expect(resolved.sections[0].presentation?.tone).toBe('immersive');
83
+ });
84
+ it('resolves template and blueprint', () => {
85
+ const catalog = {
86
+ ...emptyCatalog,
87
+ templates: {
88
+ 'product-tpl': { page: { profile: 'product-surface', themeId: 'dark' } },
89
+ },
90
+ pageBlueprints: {
91
+ 'product-bp': {
92
+ spec: {
93
+ version: '1',
94
+ id: 'blueprint',
95
+ templateId: 'product-tpl',
96
+ sections: [{ type: 'hero', titleKey: 'bp.hero' }],
97
+ },
98
+ },
99
+ },
100
+ };
101
+ const spec = {
102
+ version: '1',
103
+ id: 'my-page',
104
+ blueprintId: 'product-bp',
105
+ sections: [],
106
+ };
107
+ const resolved = resolvePageSpecWithCatalog(spec, catalog);
108
+ expect(resolved.id).toBe('my-page');
109
+ expect(resolved.profile).toBe('product-surface');
110
+ expect(resolved.themeId).toBe('dark');
111
+ expect(resolved.sections).toHaveLength(1);
112
+ expect(resolved.sections[0].titleKey).toBe('bp.hero');
113
+ });
114
+ });
115
+ //# sourceMappingURL=catalog.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.test.js","sourceRoot":"","sources":["../../src/core/catalog.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * WCAG Contrast Checker — pure luminance-based contrast ratio calculation.
3
+ * Implements WCAG 2.1 relative luminance and contrast ratio formulas.
4
+ * No DOM, no canvas.
5
+ */
6
+ export type ContrastLevel = 'AA' | 'AAA';
7
+ export type ContrastResult = {
8
+ ratio: number;
9
+ passes: boolean;
10
+ level: ContrastLevel;
11
+ foreground: string;
12
+ background: string;
13
+ };
14
+ /**
15
+ * Parse a hex color string to [r, g, b] (0-255).
16
+ * Supports #RGB, #RRGGBB, #RRGGBBAA.
17
+ */
18
+ export declare function parseHexColor(hex: string): [number, number, number] | null;
19
+ /**
20
+ * Parse rgb(r, g, b) or rgba(r, g, b, a) to [r, g, b].
21
+ */
22
+ export declare function parseRgbColor(rgb: string): [number, number, number] | null;
23
+ /**
24
+ * Parse a color string (hex or rgb/rgba) to [r, g, b].
25
+ */
26
+ export declare function parseColor(color: string): [number, number, number] | null;
27
+ /**
28
+ * Compute relative luminance per WCAG 2.1.
29
+ * https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
30
+ */
31
+ export declare function relativeLuminance(r: number, g: number, b: number): number;
32
+ /**
33
+ * Compute contrast ratio between two luminance values.
34
+ * https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
35
+ */
36
+ export declare function contrastRatio(l1: number, l2: number): number;
37
+ /**
38
+ * Check WCAG contrast between two colors.
39
+ * Returns ratio, pass/fail, and the level tested.
40
+ */
41
+ export declare function checkWcagContrast(foreground: string, background: string, level?: ContrastLevel): ContrastResult;
42
+ //# sourceMappingURL=contrast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contrast.d.ts","sourceRoot":"","sources":["../../src/core/contrast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,KAAK,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,aAAa,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiB1E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAI1E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAKzE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQzE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAI5D;AAUD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,aAAoB,GAC5B,cAAc,CAmBhB"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * WCAG Contrast Checker — pure luminance-based contrast ratio calculation.
3
+ * Implements WCAG 2.1 relative luminance and contrast ratio formulas.
4
+ * No DOM, no canvas.
5
+ */
6
+ /**
7
+ * Parse a hex color string to [r, g, b] (0-255).
8
+ * Supports #RGB, #RRGGBB, #RRGGBBAA.
9
+ */
10
+ export function parseHexColor(hex) {
11
+ const clean = hex.replace('#', '');
12
+ if (clean.length === 3) {
13
+ return [
14
+ parseInt(clean[0] + clean[0], 16),
15
+ parseInt(clean[1] + clean[1], 16),
16
+ parseInt(clean[2] + clean[2], 16),
17
+ ];
18
+ }
19
+ if (clean.length === 6 || clean.length === 8) {
20
+ return [
21
+ parseInt(clean.slice(0, 2), 16),
22
+ parseInt(clean.slice(2, 4), 16),
23
+ parseInt(clean.slice(4, 6), 16),
24
+ ];
25
+ }
26
+ return null;
27
+ }
28
+ /**
29
+ * Parse rgb(r, g, b) or rgba(r, g, b, a) to [r, g, b].
30
+ */
31
+ export function parseRgbColor(rgb) {
32
+ const match = rgb.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
33
+ if (!match)
34
+ return null;
35
+ return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
36
+ }
37
+ /**
38
+ * Parse a color string (hex or rgb/rgba) to [r, g, b].
39
+ */
40
+ export function parseColor(color) {
41
+ const trimmed = color.trim().toLowerCase();
42
+ if (trimmed.startsWith('#'))
43
+ return parseHexColor(trimmed);
44
+ if (trimmed.startsWith('rgb'))
45
+ return parseRgbColor(trimmed);
46
+ return null;
47
+ }
48
+ /**
49
+ * Compute relative luminance per WCAG 2.1.
50
+ * https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
51
+ */
52
+ export function relativeLuminance(r, g, b) {
53
+ const [rs, gs, bs] = [r, g, b].map(c => {
54
+ const sRGB = c / 255;
55
+ return sRGB <= 0.04045
56
+ ? sRGB / 12.92
57
+ : Math.pow((sRGB + 0.055) / 1.055, 2.4);
58
+ });
59
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
60
+ }
61
+ /**
62
+ * Compute contrast ratio between two luminance values.
63
+ * https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
64
+ */
65
+ export function contrastRatio(l1, l2) {
66
+ const lighter = Math.max(l1, l2);
67
+ const darker = Math.min(l1, l2);
68
+ return (lighter + 0.05) / (darker + 0.05);
69
+ }
70
+ /**
71
+ * Minimum contrast ratios per WCAG 2.1 for normal text.
72
+ */
73
+ const MIN_RATIO = {
74
+ AA: 4.5,
75
+ AAA: 7.0,
76
+ };
77
+ /**
78
+ * Check WCAG contrast between two colors.
79
+ * Returns ratio, pass/fail, and the level tested.
80
+ */
81
+ export function checkWcagContrast(foreground, background, level = 'AA') {
82
+ const fg = parseColor(foreground);
83
+ const bg = parseColor(background);
84
+ if (!fg || !bg) {
85
+ return { ratio: 0, passes: false, level, foreground, background };
86
+ }
87
+ const fgLum = relativeLuminance(...fg);
88
+ const bgLum = relativeLuminance(...bg);
89
+ const ratio = contrastRatio(fgLum, bgLum);
90
+ return {
91
+ ratio: Math.round(ratio * 100) / 100,
92
+ passes: ratio >= MIN_RATIO[level],
93
+ level,
94
+ foreground,
95
+ background,
96
+ };
97
+ }
98
+ //# sourceMappingURL=contrast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contrast.js","sourceRoot":"","sources":["../../src/core/contrast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contrast.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contrast.test.d.ts","sourceRoot":"","sources":["../../src/core/contrast.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,82 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseHexColor, parseRgbColor, parseColor, relativeLuminance, contrastRatio, checkWcagContrast, } from './contrast.js';
3
+ describe('parseHexColor', () => {
4
+ it('parses #RGB shorthand', () => {
5
+ expect(parseHexColor('#fff')).toEqual([255, 255, 255]);
6
+ expect(parseHexColor('#000')).toEqual([0, 0, 0]);
7
+ });
8
+ it('parses #RRGGBB', () => {
9
+ expect(parseHexColor('#ff0000')).toEqual([255, 0, 0]);
10
+ expect(parseHexColor('#00ff00')).toEqual([0, 255, 0]);
11
+ });
12
+ it('parses #RRGGBBAA (ignores alpha)', () => {
13
+ expect(parseHexColor('#ff000080')).toEqual([255, 0, 0]);
14
+ });
15
+ it('returns null for invalid', () => {
16
+ expect(parseHexColor('#gg')).toBeNull();
17
+ });
18
+ });
19
+ describe('parseRgbColor', () => {
20
+ it('parses rgb()', () => {
21
+ expect(parseRgbColor('rgb(255, 0, 0)')).toEqual([255, 0, 0]);
22
+ });
23
+ it('parses rgba()', () => {
24
+ expect(parseRgbColor('rgba(0, 128, 255, 0.5)')).toEqual([0, 128, 255]);
25
+ });
26
+ it('returns null for invalid', () => {
27
+ expect(parseRgbColor('hsl(0, 100%, 50%)')).toBeNull();
28
+ });
29
+ });
30
+ describe('parseColor', () => {
31
+ it('delegates to hex parser', () => {
32
+ expect(parseColor('#f00')).toEqual([255, 0, 0]);
33
+ });
34
+ it('delegates to rgb parser', () => {
35
+ expect(parseColor('rgb(0, 0, 0)')).toEqual([0, 0, 0]);
36
+ });
37
+ it('returns null for unsupported', () => {
38
+ expect(parseColor('red')).toBeNull();
39
+ });
40
+ });
41
+ describe('relativeLuminance', () => {
42
+ it('white has luminance ~1', () => {
43
+ expect(relativeLuminance(255, 255, 255)).toBeCloseTo(1.0, 2);
44
+ });
45
+ it('black has luminance 0', () => {
46
+ expect(relativeLuminance(0, 0, 0)).toBe(0);
47
+ });
48
+ });
49
+ describe('contrastRatio', () => {
50
+ it('black on white = 21:1', () => {
51
+ const ratio = contrastRatio(1.0, 0.0);
52
+ expect(ratio).toBeCloseTo(21, 0);
53
+ });
54
+ it('same color = 1:1', () => {
55
+ expect(contrastRatio(0.5, 0.5)).toBeCloseTo(1, 1);
56
+ });
57
+ });
58
+ describe('checkWcagContrast', () => {
59
+ it('black on white passes AA', () => {
60
+ const result = checkWcagContrast('#000000', '#ffffff', 'AA');
61
+ expect(result.passes).toBe(true);
62
+ expect(result.ratio).toBeGreaterThan(20);
63
+ });
64
+ it('black on white passes AAA', () => {
65
+ const result = checkWcagContrast('#000', '#fff', 'AAA');
66
+ expect(result.passes).toBe(true);
67
+ });
68
+ it('light gray on white fails AA', () => {
69
+ const result = checkWcagContrast('#cccccc', '#ffffff', 'AA');
70
+ expect(result.passes).toBe(false);
71
+ });
72
+ it('returns ratio 0 for unparseable colors', () => {
73
+ const result = checkWcagContrast('invalid', '#fff');
74
+ expect(result.ratio).toBe(0);
75
+ expect(result.passes).toBe(false);
76
+ });
77
+ it('defaults to AA level', () => {
78
+ const result = checkWcagContrast('#000', '#fff');
79
+ expect(result.level).toBe('AA');
80
+ });
81
+ });
82
+ //# sourceMappingURL=contrast.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contrast.test.js","sourceRoot":"","sources":["../../src/core/contrast.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CSS Named Colors — the 148 standard CSS color keywords.
3
+ * Used by checkNoNamedColor() to detect hardcoded color names in CSS.
4
+ * Source: https://www.w3.org/TR/css-color-4/#named-colors + transparent/currentcolor
5
+ */
6
+ export declare const CSS_NAMED_COLORS: Set<string>;
7
+ //# sourceMappingURL=css-named-colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-named-colors.d.ts","sourceRoot":"","sources":["../../src/core/css-named-colors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,gBAAgB,aAsC3B,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * CSS Named Colors — the 148 standard CSS color keywords.
3
+ * Used by checkNoNamedColor() to detect hardcoded color names in CSS.
4
+ * Source: https://www.w3.org/TR/css-color-4/#named-colors + transparent/currentcolor
5
+ */
6
+ export const CSS_NAMED_COLORS = new Set([
7
+ 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
8
+ 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
9
+ 'blueviolet', 'brown', 'burlywood',
10
+ 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue',
11
+ 'cornsilk', 'crimson', 'cyan',
12
+ 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen',
13
+ 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange',
14
+ 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue',
15
+ 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet',
16
+ 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue',
17
+ 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia',
18
+ 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green',
19
+ 'greenyellow', 'grey',
20
+ 'honeydew', 'hotpink',
21
+ 'indianred', 'indigo', 'ivory',
22
+ 'khaki',
23
+ 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue',
24
+ 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen',
25
+ 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
26
+ 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
27
+ 'lime', 'limegreen', 'linen',
28
+ 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid',
29
+ 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
30
+ 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
31
+ 'mistyrose', 'moccasin',
32
+ 'navajowhite', 'navy',
33
+ 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid',
34
+ 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
35
+ 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple',
36
+ 'rebeccapurple', 'red', 'rosybrown', 'royalblue',
37
+ 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna',
38
+ 'silver', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow',
39
+ 'springgreen', 'steelblue',
40
+ 'tan', 'teal', 'thistle', 'tomato', 'turquoise',
41
+ 'violet',
42
+ 'wheat', 'white', 'whitesmoke',
43
+ 'yellow', 'yellowgreen',
44
+ ]);
45
+ //# sourceMappingURL=css-named-colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-named-colors.js","sourceRoot":"","sources":["../../src/core/css-named-colors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IACtC,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO;IAC1D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM;IACpD,YAAY,EAAE,OAAO,EAAE,WAAW;IAClC,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB;IACjE,UAAU,EAAE,SAAS,EAAE,MAAM;IAC7B,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW;IAChE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY;IACtE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe;IACtE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY;IAC/D,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY;IAC7D,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS;IACpD,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO;IAC/D,aAAa,EAAE,MAAM;IACrB,UAAU,EAAE,SAAS;IACrB,WAAW,EAAE,QAAQ,EAAE,OAAO;IAC9B,OAAO;IACP,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW;IACrE,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,WAAW,EAAE,YAAY;IAC5E,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc;IACxE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa;IACnE,MAAM,EAAE,WAAW,EAAE,OAAO;IAC5B,SAAS,EAAE,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAE,cAAc;IACrE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB;IACxE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW;IACjE,WAAW,EAAE,UAAU;IACvB,aAAa,EAAE,MAAM;IACrB,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ;IAChE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY;IAC5E,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ;IAC3D,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW;IAChD,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ;IACvE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM;IAClE,aAAa,EAAE,WAAW;IAC1B,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW;IAC/C,QAAQ;IACR,OAAO,EAAE,OAAO,EAAE,YAAY;IAC9B,QAAQ,EAAE,aAAa;CACxB,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Core Guardrails — the 10 Sovereign Laws of Valentino Engine.
3
+ * Each law is a programmatic check, not just documentation.
4
+ */
5
+ export declare const GUARDRAILS: {
6
+ /** G1: No hardcoded px values in component overrides */
7
+ readonly NO_HARDCODED_PX: RegExp;
8
+ /** G2: No RGBA or HEX hardcoded colors */
9
+ readonly NO_HARDCODED_COLOR: RegExp;
10
+ /** G3: No direct DOM style manipulation */
11
+ readonly NO_INLINE_STYLE: RegExp;
12
+ };
13
+ export declare function checkNoHardcodedPx(css: string): string[];
14
+ export declare function checkNoHardcodedColor(css: string): string[];
15
+ export declare function checkNoNamedColor(css: string): string[];
16
+ //# sourceMappingURL=guardrails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,UAAU;IACrB,wDAAwD;;IAGxD,0CAA0C;;IAG1C,2CAA2C;;CAEnC,CAAC;AAEX,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CASxD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAS3D;AASD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAcvD"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Core Guardrails — the 10 Sovereign Laws of Valentino Engine.
3
+ * Each law is a programmatic check, not just documentation.
4
+ */
5
+ import { CSS_NAMED_COLORS } from './css-named-colors.js';
6
+ export const GUARDRAILS = {
7
+ /** G1: No hardcoded px values in component overrides */
8
+ NO_HARDCODED_PX: /(?<!\d)(\d+)px(?!\s*[/*])/,
9
+ /** G2: No RGBA or HEX hardcoded colors */
10
+ NO_HARDCODED_COLOR: /#[0-9a-fA-F]{3,8}|rgba?\(/,
11
+ /** G3: No direct DOM style manipulation */
12
+ NO_INLINE_STYLE: /\.style\.(padding|margin|color|background)\s*=/,
13
+ };
14
+ export function checkNoHardcodedPx(css) {
15
+ const violations = [];
16
+ const lines = css.split('\n');
17
+ lines.forEach((line, i) => {
18
+ if (GUARDRAILS.NO_HARDCODED_PX.test(line)) {
19
+ violations.push(`Line ${i + 1}: Hardcoded px detected — use --valentino-rhythm-* variables. "${line.trim()}"`);
20
+ }
21
+ });
22
+ return violations;
23
+ }
24
+ export function checkNoHardcodedColor(css) {
25
+ const violations = [];
26
+ const lines = css.split('\n');
27
+ lines.forEach((line, i) => {
28
+ if (GUARDRAILS.NO_HARDCODED_COLOR.test(line)) {
29
+ violations.push(`Line ${i + 1}: Hardcoded color detected — use CSS root variables. "${line.trim()}"`);
30
+ }
31
+ });
32
+ return violations;
33
+ }
34
+ /**
35
+ * Detect CSS named colors (red, blue, navy, etc.) used as property values.
36
+ * Matches after a colon in CSS declarations to avoid false positives in
37
+ * selectors, comments, or class names.
38
+ */
39
+ const CSS_VALUE_WORD_RE = /:\s*([a-z]+)\s*[;!}]/gi;
40
+ export function checkNoNamedColor(css) {
41
+ const violations = [];
42
+ const lines = css.split('\n');
43
+ lines.forEach((line, i) => {
44
+ CSS_VALUE_WORD_RE.lastIndex = 0;
45
+ let match;
46
+ while ((match = CSS_VALUE_WORD_RE.exec(line)) !== null) {
47
+ const word = match[1].toLowerCase();
48
+ if (CSS_NAMED_COLORS.has(word)) {
49
+ violations.push(`Line ${i + 1}: Named CSS color "${word}" detected — use CSS root variables. "${line.trim()}"`);
50
+ }
51
+ }
52
+ });
53
+ return violations;
54
+ }
55
+ //# sourceMappingURL=guardrails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.js","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,wDAAwD;IACxD,eAAe,EAAE,2BAA2B;IAE5C,0CAA0C;IAC1C,kBAAkB,EAAE,2BAA2B;IAE/C,2CAA2C;IAC3C,eAAe,EAAE,gDAAgD;CACzD,CAAC;AAEX,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,IAAI,UAAU,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,kEAAkE,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACjH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,IAAI,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,yDAAyD,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxG,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAEnD,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,iBAAiB,CAAC,SAAS,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACvD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,sBAAsB,IAAI,yCAAyC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAClH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=guardrails.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.test.d.ts","sourceRoot":"","sources":["../../src/core/guardrails.test.ts"],"names":[],"mappings":""}