@pure-ds/core 0.4.37 → 0.5.1

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 (146) hide show
  1. package/dist/types/pds.d.ts +32 -11
  2. package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts +9 -0
  3. package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts.map +1 -0
  4. package/dist/types/public/assets/chunk-746HIXIK.d.ts +52 -0
  5. package/dist/types/public/assets/chunk-746HIXIK.d.ts.map +1 -0
  6. package/dist/types/public/assets/chunk-APJV5T3J.d.ts +106 -0
  7. package/dist/types/public/assets/chunk-APJV5T3J.d.ts.map +1 -0
  8. package/dist/types/public/assets/chunk-BEPKFFM7.d.ts +398 -0
  9. package/dist/types/public/assets/chunk-BEPKFFM7.d.ts.map +1 -0
  10. package/dist/types/public/assets/chunk-ISS7UH5H.d.ts +2424 -0
  11. package/dist/types/public/assets/chunk-ISS7UH5H.d.ts.map +1 -0
  12. package/dist/types/public/assets/chunk-RUPLQUDG.d.ts +582 -0
  13. package/dist/types/public/assets/chunk-RUPLQUDG.d.ts.map +1 -0
  14. package/dist/types/public/assets/chunk-USML4NYF.d.ts +18 -0
  15. package/dist/types/public/assets/chunk-USML4NYF.d.ts.map +1 -0
  16. package/dist/types/public/assets/chunk-Z47A3HLT.d.ts +3 -0
  17. package/dist/types/public/assets/chunk-Z47A3HLT.d.ts.map +1 -0
  18. package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts +9 -0
  19. package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts.map +1 -0
  20. package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts +88 -0
  21. package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts.map +1 -0
  22. package/dist/types/public/assets/js/chunk-746HIXIK.d.ts +52 -0
  23. package/dist/types/public/assets/js/chunk-746HIXIK.d.ts.map +1 -0
  24. package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts +4 -0
  25. package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts.map +1 -0
  26. package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts +398 -0
  27. package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts.map +1 -0
  28. package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts +1695 -0
  29. package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts.map +1 -0
  30. package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts +747 -0
  31. package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts.map +1 -0
  32. package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts +582 -0
  33. package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts.map +1 -0
  34. package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts +68 -0
  35. package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts.map +1 -0
  36. package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts +15 -0
  37. package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts.map +1 -0
  38. package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts +9 -0
  39. package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts.map +1 -0
  40. package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts +485 -0
  41. package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts.map +1 -0
  42. package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts +3 -0
  43. package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts.map +1 -0
  44. package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts +5 -0
  45. package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts.map +1 -0
  46. package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts +833 -0
  47. package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts.map +1 -0
  48. package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts +1687 -0
  49. package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts.map +1 -0
  50. package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts +5 -0
  51. package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts.map +1 -0
  52. package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts +38 -0
  53. package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts.map +1 -0
  54. package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts +4 -0
  55. package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts.map +1 -0
  56. package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts +4 -0
  57. package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts.map +1 -0
  58. package/dist/types/public/assets/js/pds-core/pds-generator.d.ts +700 -0
  59. package/dist/types/public/assets/js/pds-core/pds-generator.d.ts.map +1 -0
  60. package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts +27 -0
  61. package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts.map +1 -0
  62. package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts +3 -0
  63. package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts.map +1 -0
  64. package/dist/types/public/assets/js/pds-gen.d.ts +106 -0
  65. package/dist/types/public/assets/js/pds-gen.d.ts.map +1 -0
  66. package/dist/types/public/assets/js/pds-live.d.ts +11 -0
  67. package/dist/types/public/assets/js/pds-live.d.ts.map +1 -0
  68. package/dist/types/public/assets/js/pds-manager.d.ts +1328 -0
  69. package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -0
  70. package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts +9 -0
  71. package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts.map +1 -0
  72. package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts +70 -0
  73. package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts.map +1 -0
  74. package/dist/types/public/assets/js/pds.d.ts +2 -18
  75. package/dist/types/public/assets/js/pds.d.ts.map +1 -1
  76. package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts +9 -0
  77. package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts.map +1 -0
  78. package/dist/types/src/js/common/pds-core/pds-config.d.ts +757 -0
  79. package/dist/types/src/js/common/pds-core/pds-config.d.ts.map +1 -0
  80. package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts +28 -0
  81. package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts.map +1 -0
  82. package/dist/types/src/js/common/pds-core/pds-enums.d.ts +87 -0
  83. package/dist/types/src/js/common/pds-core/pds-enums.d.ts.map +1 -0
  84. package/dist/types/src/js/common/pds-core/pds-generator.d.ts +700 -0
  85. package/dist/types/src/js/common/pds-core/pds-generator.d.ts.map +1 -0
  86. package/dist/types/src/js/common/pds-core/pds-ontology.d.ts +380 -0
  87. package/dist/types/src/js/common/pds-core/pds-ontology.d.ts.map +1 -0
  88. package/dist/types/src/js/common/pds-core/pds-paths.d.ts +37 -0
  89. package/dist/types/src/js/common/pds-core/pds-paths.d.ts.map +1 -0
  90. package/dist/types/src/js/common/pds-core/pds-query.d.ts +102 -0
  91. package/dist/types/src/js/common/pds-core/pds-query.d.ts.map +1 -0
  92. package/dist/types/src/js/common/pds-core/pds-registry.d.ts +35 -0
  93. package/dist/types/src/js/common/pds-core/pds-registry.d.ts.map +1 -0
  94. package/dist/types/src/js/common/pds-core/pds-utilities.d.ts +27 -0
  95. package/dist/types/src/js/common/pds-core/pds-utilities.d.ts.map +1 -0
  96. package/dist/types/src/js/pds-core/pds-generator.d.ts +38 -46
  97. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
  98. package/dist/types/src/js/pds-core/pds-live.d.ts +39 -0
  99. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -0
  100. package/dist/types/src/js/pds-core/pds-runtime.d.ts +39 -0
  101. package/dist/types/src/js/pds-core/pds-runtime.d.ts.map +1 -0
  102. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +60 -0
  103. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -0
  104. package/dist/types/src/js/pds-core/pds-utilities.d.ts +27 -0
  105. package/dist/types/src/js/pds-core/pds-utilities.d.ts.map +1 -0
  106. package/dist/types/src/js/pds-gen.d.ts +48 -0
  107. package/dist/types/src/js/pds-gen.d.ts.map +1 -0
  108. package/dist/types/src/js/pds-live-runtime.d.ts +7 -0
  109. package/dist/types/src/js/pds-live-runtime.d.ts.map +1 -0
  110. package/dist/types/src/js/pds-live-validation.d.ts +44 -0
  111. package/dist/types/src/js/pds-live-validation.d.ts.map +1 -0
  112. package/dist/types/src/js/pds-live.d.ts +11 -0
  113. package/dist/types/src/js/pds-live.d.ts.map +1 -0
  114. package/dist/types/src/js/pds-manager.d.ts +2 -0
  115. package/dist/types/src/js/pds-manager.d.ts.map +1 -0
  116. package/dist/types/src/js/pds.d.ts +6 -33
  117. package/dist/types/src/js/pds.d.ts.map +1 -1
  118. package/package.json +11 -12
  119. package/packages/pds-cli/bin/{generate-css-data.mjs → generate-css-data.js} +563 -563
  120. package/packages/pds-cli/bin/{generate-manifest.mjs → generate-manifest.js} +352 -352
  121. package/packages/pds-cli/bin/{pds-build-icons.mjs → pds-build-icons.js} +152 -152
  122. package/packages/pds-cli/bin/{pds-dx.mjs → pds-dx.js} +114 -114
  123. package/packages/pds-cli/bin/{pds-init-config.mjs → pds-init-config.js} +34 -34
  124. package/packages/pds-cli/bin/{pds-setup-copilot.mjs → pds-setup-copilot.js} +106 -106
  125. package/packages/pds-cli/bin/{pds-static.mjs → pds-static.js} +581 -581
  126. package/packages/pds-cli/bin/{pds.mjs → pds.js} +127 -127
  127. package/packages/pds-cli/bin/postinstall.mjs +522 -563
  128. package/packages/pds-cli/bin/{sync-assets.mjs → sync-assets.js} +251 -251
  129. package/packages/pds-cli/lib/{asset-roots.mjs → asset-roots.js} +47 -47
  130. package/packages/pds-cli/lib/{fs-writer.mjs → fs-writer.js} +75 -75
  131. package/public/assets/js/app.js +95 -118
  132. package/public/assets/js/pds-manager.js +3251 -0
  133. package/public/assets/js/pds.js +10 -3201
  134. package/readme.md +2014 -2016
  135. package/src/js/pds-core/pds-enhancers.js +518 -518
  136. package/src/js/pds-core/pds-enums.js +86 -86
  137. package/src/js/pds-core/pds-generator.js +255 -185
  138. package/src/js/pds-core/pds-live.js +434 -0
  139. package/src/js/pds-core/pds-paths.js +109 -109
  140. package/src/js/pds-core/pds-registry.js +79 -79
  141. package/src/js/pds-core/pds-runtime.js +184 -0
  142. package/src/js/pds-core/pds-start-helpers.js +404 -0
  143. package/src/js/pds.d.ts +32 -11
  144. package/src/js/pds.js +37 -1182
  145. package/getting-started.md +0 -626
  146. package/src/js/pds-core/pds.d.ts +0 -129
@@ -1,86 +1,86 @@
1
- // Static enums for design system values
2
- export const enums = {
3
- FontWeights: {
4
- light: 300,
5
- normal: 400,
6
- medium: 500,
7
- semibold: 600,
8
- bold: 700,
9
- },
10
- LineHeights: {
11
- tight: 1.25,
12
- normal: 1.5,
13
- relaxed: 1.75,
14
- },
15
-
16
- BorderWidths: {
17
- hairline: 0.5,
18
- thin: 1,
19
- medium: 2,
20
- thick: 3,
21
- },
22
-
23
- RadiusSizes: {
24
- none: 0,
25
- small: 4,
26
- medium: 8,
27
- large: 16,
28
- xlarge: 24,
29
- xxlarge: 32
30
- },
31
-
32
- ShadowDepths: {
33
- none: "none",
34
- light: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
35
- medium:
36
- "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
37
- deep: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
38
- extreme: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
39
- },
40
- TransitionSpeeds: {
41
- fast: 150,
42
- normal: 250,
43
- slow: 350,
44
- },
45
-
46
- AnimationEasings: {
47
- linear: "linear",
48
- ease: "ease",
49
- "ease-in": "ease-in",
50
- "ease-out": "ease-out",
51
- "ease-in-out": "ease-in-out",
52
- bounce: "cubic-bezier(0.68, -0.55, 0.265, 1.55)",
53
- },
54
- TouchTargetSizes: {
55
- compact: 36,
56
- standard: 44, // iOS/Android accessibility standard
57
- comfortable: 48,
58
- spacious: 56,
59
- },
60
-
61
- LinkStyles: {
62
- inline: "inline", // Normal inline text links
63
- block: "block", // Block-level links
64
- button: "button", // Button-like links (flex with touch target)
65
- },
66
-
67
- FocusStyles: {
68
- ring: "ring", // Box-shadow ring (default)
69
- outline: "outline", // Browser outline
70
- border: "border", // Border change
71
- glow: "glow", // Subtle glow effect
72
- },
73
-
74
- TabSizes: {
75
- compact: 2,
76
- standard: 4,
77
- wide: 8,
78
- },
79
-
80
- SelectIcons: {
81
- chevron: "chevron", // Standard chevron down
82
- arrow: "arrow", // Simple arrow
83
- caret: "caret", // Triangle caret
84
- none: "none", // No icon
85
- },
86
- };
1
+ // Static enums for design system values
2
+ export const enums = {
3
+ FontWeights: {
4
+ light: 300,
5
+ normal: 400,
6
+ medium: 500,
7
+ semibold: 600,
8
+ bold: 700,
9
+ },
10
+ LineHeights: {
11
+ tight: 1.25,
12
+ normal: 1.5,
13
+ relaxed: 1.75,
14
+ },
15
+
16
+ BorderWidths: {
17
+ hairline: 0.5,
18
+ thin: 1,
19
+ medium: 2,
20
+ thick: 3,
21
+ },
22
+
23
+ RadiusSizes: {
24
+ none: 0,
25
+ small: 4,
26
+ medium: 8,
27
+ large: 16,
28
+ xlarge: 24,
29
+ xxlarge: 32
30
+ },
31
+
32
+ ShadowDepths: {
33
+ none: "none",
34
+ light: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
35
+ medium:
36
+ "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
37
+ deep: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
38
+ extreme: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
39
+ },
40
+ TransitionSpeeds: {
41
+ fast: 150,
42
+ normal: 250,
43
+ slow: 350,
44
+ },
45
+
46
+ AnimationEasings: {
47
+ linear: "linear",
48
+ ease: "ease",
49
+ "ease-in": "ease-in",
50
+ "ease-out": "ease-out",
51
+ "ease-in-out": "ease-in-out",
52
+ bounce: "cubic-bezier(0.68, -0.55, 0.265, 1.55)",
53
+ },
54
+ TouchTargetSizes: {
55
+ compact: 36,
56
+ standard: 44, // iOS/Android accessibility standard
57
+ comfortable: 48,
58
+ spacious: 56,
59
+ },
60
+
61
+ LinkStyles: {
62
+ inline: "inline", // Normal inline text links
63
+ block: "block", // Block-level links
64
+ button: "button", // Button-like links (flex with touch target)
65
+ },
66
+
67
+ FocusStyles: {
68
+ ring: "ring", // Box-shadow ring (default)
69
+ outline: "outline", // Browser outline
70
+ border: "border", // Border change
71
+ glow: "glow", // Subtle glow effect
72
+ },
73
+
74
+ TabSizes: {
75
+ compact: 2,
76
+ standard: 4,
77
+ wide: 8,
78
+ },
79
+
80
+ SelectIcons: {
81
+ chevron: "chevron", // Standard chevron down
82
+ arrow: "arrow", // Simple arrow
83
+ caret: "caret", // Triangle caret
84
+ none: "none", // No icon
85
+ },
86
+ };
@@ -1,4 +1,4 @@
1
- import { registry as pdsRegistry } from "./pds-registry.js";
1
+ import { presets } from "./pds-config.js";
2
2
 
3
3
  /**
4
4
  * Generator - A JS-config-first design system
@@ -4987,216 +4987,286 @@ export const ${name}CSS = \`${escapedCSS}\`;
4987
4987
  `;
4988
4988
  }
4989
4989
 
4990
- /**
4991
- * Static method to apply styles to document
4992
- * Creates a link element with BLOB URL
4993
- * @param {Generator} [generator] - Optional Generator instance (defaults to singleton)
4994
- */
4995
- static applyStyles(generator) {
4996
- // Use provided generator or singleton instance
4997
- const target = generator || Generator.instance;
4998
-
4999
- // Validate parameter
5000
- if (!target || typeof target !== "object") {
5001
- console.error("[Generator] applyStyles requires a generator object or active singleton");
5002
- return;
5003
- }
5004
-
5005
- // Preferred: apply layered CSS so tokens + primitives + components + utilities
5006
- // are available in light DOM (ensures primitives like :where(button):active apply)
5007
- const cssText = target.layeredCSS || target.css || "";
5008
- if (!cssText) {
5009
- target.options?.log?.(
5010
- "warn",
5011
- "[Generator] No CSS available on designer to apply"
5012
- );
5013
- return;
5014
- }
5015
-
5016
- // Install/update runtime styles atomically to avoid flicker caused by
5017
- // creating/removing <link> or swapping blob URLs.
5018
- Generator.installRuntimeStyles(cssText);
5019
- // if (designer && designer.#blobURLs && designer.options?.debug) {
5020
- // designer.options?.log?.(
5021
- // "debug",
5022
- // "[Generator] Applied live styles via in-place stylesheet"
5023
- // );
5024
- // }
5025
- }
5026
-
5027
- /**
5028
- * Install runtime styles for PDS using constructable stylesheets when
5029
- * available, otherwise update a single <style id="pds-runtime-stylesheet">.
5030
- * This approach reduces flicker and avoids link/blob swapping.
5031
- */
5032
- static installRuntimeStyles(cssText) {
5033
- //console.log(cssText);
5034
- try {
5035
- if (typeof document === "undefined") return; // server-side guard
4990
+ }
5036
4991
 
5037
- // Preferred: constructable stylesheet (fast, atomic)
5038
- if (
5039
- typeof CSSStyleSheet !== "undefined" &&
5040
- "adoptedStyleSheets" in Document.prototype
5041
- ) {
5042
- const sheet = new CSSStyleSheet();
5043
- // replaceSync is synchronous and atomic for the stylesheet
5044
- sheet.replaceSync(cssText);
5045
4992
 
5046
- // Tag it so we can keep existing non-PDS sheets
5047
- sheet._pds = true;
4993
+ /**
4994
+ * Validate a design configuration for accessibility sanity checks.
4995
+ * Currently validates color contrast for primary buttons and base surface text
4996
+ * in both light and dark themes.
4997
+ *
4998
+ * @param {object} designConfig - A full or partial PDS config object
4999
+ * @param {object} [options]
5000
+ * @param {number} [options.minContrast=4.5] - Minimum contrast ratio for normal text
5001
+ * @returns {{ ok: boolean, issues: Array<{path:string, message:string, ratio:number, min:number, context?:string}> }}
5002
+ */
5003
+ export function validateDesign(designConfig = {}, options = {}) {
5004
+ const MIN = Number(options.minContrast || 4.5);
5048
5005
 
5049
- const others = (document.adoptedStyleSheets || []).filter(
5050
- (s) => s._pds !== true
5051
- );
5052
- document.adoptedStyleSheets = [...others, sheet];
5006
+ // Local helpers (keep public; no dependency on private Generator methods)
5007
+ const hexToRgb = (hex) => {
5008
+ const h = String(hex || "").replace("#", "");
5009
+ const full =
5010
+ h.length === 3
5011
+ ? h
5012
+ .split("")
5013
+ .map((c) => c + c)
5014
+ .join("")
5015
+ : h;
5016
+ const num = parseInt(full || "0", 16);
5017
+ return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };
5018
+ };
5019
+ const luminance = (hex) => {
5020
+ const { r, g, b } = hexToRgb(hex);
5021
+ const srgb = [r / 255, g / 255, b / 255].map((v) =>
5022
+ v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
5023
+ );
5024
+ return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
5025
+ };
5026
+ const contrast = (a, b) => {
5027
+ if (!a || !b) return 0;
5028
+ const L1 = luminance(a);
5029
+ const L2 = luminance(b);
5030
+ const lighter = Math.max(L1, L2);
5031
+ const darker = Math.min(L1, L2);
5032
+ return (lighter + 0.05) / (darker + 0.05);
5033
+ };
5053
5034
 
5054
- // Keep a reference
5055
- Generator.__pdsRuntimeSheet = sheet;
5056
- return;
5057
- }
5035
+ const issues = [];
5036
+ try {
5037
+ // Build tokens from the candidate config
5038
+ const gen = new Generator({ design: structuredClone(designConfig) });
5039
+ const c = gen.tokens.colors;
5040
+
5041
+ // Light theme checks - use computed interactive tokens
5042
+ const light = {
5043
+ surfaceBg: c.surface?.base,
5044
+ surfaceText: c.gray?.[900] || "#000000",
5045
+ primaryFill: c.interactive?.light?.fill || c.primary?.[600],
5046
+ primaryText: c.interactive?.light?.text || c.primary?.[600],
5047
+ };
5058
5048
 
5059
- // Fallback: single <style> element in the document head
5060
- const styleId = "pds-runtime-stylesheet";
5061
- let el = document.getElementById(styleId);
5062
- if (!el) {
5063
- el = document.createElement("style");
5064
- el.id = styleId;
5065
- el.type = "text/css";
5066
- const head = document.head || document.getElementsByTagName("head")[0];
5067
- if (head) head.appendChild(el);
5068
- else document.documentElement.appendChild(el);
5069
- }
5049
+ // Primary button (light): check button fill with white text
5050
+ const lightBtnRatio = contrast(light.primaryFill, "#ffffff");
5051
+ if (lightBtnRatio < MIN) {
5052
+ issues.push({
5053
+ path: "/colors/primary",
5054
+ message: `Primary button contrast too low in light theme (${lightBtnRatio.toFixed(
5055
+ 2
5056
+ )} < ${MIN}). Choose a darker primary.`,
5057
+ ratio: lightBtnRatio,
5058
+ min: MIN,
5059
+ context: "light/btn-primary",
5060
+ });
5061
+ }
5070
5062
 
5071
- // Update the stylesheet content in place
5072
- el.textContent = cssText;
5073
- } catch (err) {
5074
- // No access to config here in static method, fall back to console
5075
- console.warn("Generator.installRuntimeStyles failed:", err);
5063
+ // Surface text (light): text vs surface base
5064
+ const lightTextRatio = contrast(light.surfaceBg, light.surfaceText);
5065
+ if (lightTextRatio < MIN) {
5066
+ issues.push({
5067
+ path: "/colors/background",
5068
+ message: `Base text contrast on surface (light) is too low (${lightTextRatio.toFixed(
5069
+ 2
5070
+ )} < ${MIN}). Adjust background or secondary (gray).`,
5071
+ ratio: lightTextRatio,
5072
+ min: MIN,
5073
+ context: "light/surface-text",
5074
+ });
5076
5075
  }
5077
- }
5078
- }
5079
5076
 
5080
- // ============================================================================
5081
- // PDS ADOPTER - Helper for web components
5082
- // ============================================================================
5077
+ // Primary text for outline/link: check link text on surface
5078
+ const lightOutlineRatio = contrast(light.primaryText, light.surfaceBg);
5079
+ if (lightOutlineRatio < MIN) {
5080
+ issues.push({
5081
+ path: "/colors/primary",
5082
+ message: `Primary text on surface is too low for outline/link styles (light) (${lightOutlineRatio.toFixed(
5083
+ 2
5084
+ )} < ${MIN}). Choose a darker primary or lighter surface.`,
5085
+ ratio: lightOutlineRatio,
5086
+ min: MIN,
5087
+ context: "light/outline",
5088
+ });
5089
+ }
5083
5090
 
5084
- /**
5085
- * Adopt primitives stylesheet into a shadow root
5086
- * This is the primary method components should use
5087
- *
5088
- * @param {ShadowRoot} shadowRoot - The shadow root to adopt into
5089
- * @param {CSSStyleSheet[]} additionalSheets - Additional component-specific stylesheets
5090
- * @returns {Promise<void>}
5091
- *
5092
- * @example
5093
- * // In your web component:
5094
- * import { PDS } from 'pure-ds';
5095
- *
5096
- * async connectedCallback() {
5097
- * this.attachShadow({ mode: 'open' });
5098
- *
5099
- * const componentStyles = new CSSStyleSheet();
5100
- * componentStyles.replaceSync(`...your styles...`);
5101
- *
5102
- * await PDS.adoptPrimitives(this.shadowRoot, [componentStyles]);
5103
- * }
5104
- */
5105
- export async function adoptPrimitives(shadowRoot, additionalSheets = []) {
5106
- try {
5107
- // Get primitives stylesheet (live or static)
5108
- const primitives = await PDS.registry.getStylesheet("primitives");
5091
+ // Dark theme checks - use computed interactive tokens
5092
+ const d = c.dark;
5093
+ if (d) {
5094
+ const dark = {
5095
+ surfaceBg: d.surface?.base || c.surface?.inverse,
5096
+ primaryFill: c.interactive?.dark?.fill || d.primary?.[600],
5097
+ primaryText: c.interactive?.dark?.text || d.primary?.[600],
5098
+ };
5109
5099
 
5110
- // Adopt primitives + additional sheets
5111
- shadowRoot.adoptedStyleSheets = [primitives, ...additionalSheets];
5100
+ // Primary button (dark): check button fill with white text
5101
+ const darkBtnRatio = contrast(dark.primaryFill, "#ffffff");
5102
+ if (darkBtnRatio < MIN) {
5103
+ issues.push({
5104
+ path: "/colors/darkMode/primary",
5105
+ message: `Primary button contrast too low in dark theme (${darkBtnRatio.toFixed(
5106
+ 2
5107
+ )} < ${MIN}). Override darkMode.primary or pick a brighter hue.`,
5108
+ ratio: darkBtnRatio,
5109
+ min: MIN,
5110
+ context: "dark/btn-primary",
5111
+ });
5112
+ }
5112
5113
 
5113
- if (PDS.registry.isLive) {
5114
- const componentName =
5115
- shadowRoot.host?.tagName?.toLowerCase() || "unknown";
5114
+ // Outline/link style in dark: check link text on dark surface
5115
+ const darkOutlineRatio = contrast(dark.primaryText, dark.surfaceBg);
5116
+ if (darkOutlineRatio < MIN) {
5117
+ issues.push({
5118
+ path: "/colors/darkMode/primary",
5119
+ message: `Primary text on surface is too low for outline/link styles (dark) (${darkOutlineRatio.toFixed(
5120
+ 2
5121
+ )} < ${MIN}). Override darkMode.primary/background.`,
5122
+ ratio: darkOutlineRatio,
5123
+ min: MIN,
5124
+ context: "dark/outline",
5125
+ });
5126
+ }
5116
5127
  }
5117
- } catch (error) {
5118
- const componentName = shadowRoot.host?.tagName?.toLowerCase() || "unknown";
5119
- // No access to config in this context, fall back to console
5120
- console.error(
5121
- `[PDS Adopter] <${componentName}> failed to adopt primitives:`,
5122
- error
5123
- );
5124
- // Continue with just additional sheets as fallback
5125
- shadowRoot.adoptedStyleSheets = additionalSheets;
5128
+ } catch (err) {
5129
+ issues.push({
5130
+ path: "/",
5131
+ message: `Validation failed: ${String(err?.message || err)}`,
5132
+ ratio: 0,
5133
+ min: 0,
5134
+ });
5126
5135
  }
5136
+
5137
+ return { ok: issues.length === 0, issues };
5127
5138
  }
5128
5139
 
5129
5140
  /**
5130
- * Adopt multiple layers into a shadow root
5131
- * For complex components that need more than just primitives
5141
+ * Validate multiple design configurations at once.
5142
+ * Useful for build-time enforcement of preset compliance.
5132
5143
  *
5133
- * @param {ShadowRoot} shadowRoot - The shadow root to adopt into
5134
- * @param {string[]} layers - Array of layer names to adopt (e.g., ['tokens', 'primitives', 'components'])
5135
- * @param {CSSStyleSheet[]} additionalSheets - Additional component-specific stylesheets
5136
- * @returns {Promise<void>}
5144
+ * @param {Array<object>} designs - Array of design configs; items may include an optional `name` property.
5145
+ * @param {object} [options] - Options forwarded to validateDesign (e.g., { minContrast })
5146
+ * @returns {{ ok: boolean, results: Array<{ name?: string, ok: boolean, issues: Array<{path:string, message:string, ratio:number, min:number, context?:string}> }> }}
5137
5147
  */
5138
- export async function adoptLayers(
5139
- shadowRoot,
5140
- layers = ["primitives"],
5141
- additionalSheets = []
5142
- ) {
5143
- try {
5144
- // Get all requested stylesheets
5145
- const stylesheets = await Promise.all(
5146
- layers.map(async (layer) => {
5147
- // In live mode, get stylesheets directly from the Generator singleton
5148
- if (Generator.instance) {
5149
- switch (layer) {
5150
- case "tokens":
5151
- return Generator.instance.tokensStylesheet;
5152
- case "primitives":
5153
- return Generator.instance.primitivesStylesheet;
5154
- case "components":
5155
- return Generator.instance.componentsStylesheet;
5156
- case "utilities":
5157
- return Generator.instance.utilitiesStylesheet;
5158
- default:
5159
- // Fall through to registry for unknown layers or static fallback
5160
- break;
5161
- }
5148
+ export function validateDesigns(designs = [], options = {}) {
5149
+ const results = [];
5150
+
5151
+ const list = Array.isArray(designs)
5152
+ ? designs
5153
+ : designs && typeof designs === "object"
5154
+ ? Object.values(designs)
5155
+ : [];
5156
+
5157
+ for (const item of list) {
5158
+ let name;
5159
+ let configToValidate = null;
5160
+
5161
+ // Accept a few shapes:
5162
+ // - string => treat as preset id/name
5163
+ // - { preset, design?, name? } => resolve preset then merge overrides
5164
+ // - full config object (legacy) => validate directly
5165
+ if (typeof item === "string") {
5166
+ const id = String(item).toLowerCase();
5167
+ const found =
5168
+ presets?.[id] ||
5169
+ Object.values(presets || {}).find(
5170
+ (p) =>
5171
+ __slugify(p.name) === id ||
5172
+ String(p.name || "").toLowerCase() === id
5173
+ );
5174
+ if (!found) {
5175
+ results.push({
5176
+ name: item,
5177
+ ok: false,
5178
+ issues: [
5179
+ {
5180
+ path: "/",
5181
+ message: `Preset not found: ${item}`,
5182
+ ratio: 0,
5183
+ min: 0,
5184
+ },
5185
+ ],
5186
+ });
5187
+ continue;
5188
+ }
5189
+ name = found.name || id;
5190
+ configToValidate = structuredClone(found);
5191
+ } else if (item && typeof item === "object") {
5192
+ name = item.name || item.preset || undefined;
5193
+ if ("preset" in item || "design" in item) {
5194
+ const effectivePreset = String(item.preset || "default").toLowerCase();
5195
+ const found =
5196
+ presets?.[effectivePreset] ||
5197
+ Object.values(presets || {}).find(
5198
+ (p) =>
5199
+ __slugify(p.name) === effectivePreset ||
5200
+ String(p.name || "").toLowerCase() === effectivePreset
5201
+ );
5202
+ if (!found) {
5203
+ results.push({
5204
+ name,
5205
+ ok: false,
5206
+ issues: [
5207
+ {
5208
+ path: "/",
5209
+ message: `Preset not found: ${item.preset}`,
5210
+ ratio: 0,
5211
+ min: 0,
5212
+ },
5213
+ ],
5214
+ });
5215
+ continue;
5162
5216
  }
5163
- return pdsRegistry.getStylesheet(layer);
5164
- })
5165
- );
5217
+ let base = structuredClone(found);
5218
+ if (item.design && typeof item.design === "object") {
5219
+ base = __deepMerge(base, structuredClone(item.design));
5220
+ }
5221
+ configToValidate = base;
5222
+ } else {
5223
+ // Assume a full config object
5224
+ configToValidate = item;
5225
+ }
5226
+ }
5227
+
5228
+ if (!configToValidate) {
5229
+ results.push({
5230
+ name,
5231
+ ok: false,
5232
+ issues: [
5233
+ { path: "/", message: "Invalid design entry", ratio: 0, min: 0 },
5234
+ ],
5235
+ });
5236
+ continue;
5237
+ }
5166
5238
 
5167
- // Filter out any null results
5168
- const validStylesheets = stylesheets.filter((sheet) => sheet !== null);
5239
+ const { ok, issues } = validateDesign(configToValidate, options);
5240
+ results.push({ name, ok, issues });
5241
+ }
5169
5242
 
5170
- // Adopt all layers + additional sheets
5171
- shadowRoot.adoptedStyleSheets = [...validStylesheets, ...additionalSheets];
5243
+ return { ok: results.every((r) => r.ok), results };
5244
+ }
5172
5245
 
5173
- if (PDS.registry.isLive) {
5174
- const componentName =
5175
- shadowRoot.host?.tagName?.toLowerCase() || "unknown";
5246
+ // Internal: deep merge utility (arrays replace; objects merge)
5247
+ function __deepMerge(target = {}, source = {}) {
5248
+ if (!source || typeof source !== "object") return target;
5249
+ const out = Array.isArray(target) ? [...target] : { ...target };
5250
+ for (const [key, value] of Object.entries(source)) {
5251
+ if (value && typeof value === "object" && !Array.isArray(value)) {
5252
+ out[key] = __deepMerge(
5253
+ out[key] && typeof out[key] === "object" ? out[key] : {},
5254
+ value
5255
+ );
5256
+ } else {
5257
+ out[key] = value;
5176
5258
  }
5177
- } catch (error) {
5178
- const componentName = shadowRoot.host?.tagName?.toLowerCase() || "unknown";
5179
- // No access to config in this context, fall back to console
5180
- console.error(
5181
- `[PDS Adopter] <${componentName}> failed to adopt layers:`,
5182
- error
5183
- );
5184
- // Continue with just additional sheets as fallback
5185
- shadowRoot.adoptedStyleSheets = additionalSheets;
5186
5259
  }
5260
+ return out;
5187
5261
  }
5188
5262
 
5189
- /**
5190
- * Create a component-specific stylesheet from CSS string
5191
- * Helper to create constructable stylesheets
5192
- *
5193
- * @param {string} css - CSS string
5194
- * @returns {CSSStyleSheet}
5195
- */
5196
- export function createStylesheet(css) {
5197
- const sheet = new CSSStyleSheet();
5198
- sheet.replaceSync(css);
5199
- return sheet;
5263
+ // Internal: create a slug for matching names like "Paper & Ink" -> "paper-and-ink"
5264
+ function __slugify(str = "") {
5265
+ return String(str)
5266
+ .toLowerCase()
5267
+ .replace(/&/g, " and ")
5268
+ .replace(/[^a-z0-9]+/g, "-")
5269
+ .replace(/^-+|-+$/g, "");
5200
5270
  }
5201
5271
 
5202
5272