@kilivi/payloadcms-theme-management 1.1.2 → 2.1.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 (214) hide show
  1. package/README.md +318 -847
  2. package/dist/components/AppearanceSessionControls.d.ts +15 -0
  3. package/dist/components/AppearanceSessionControls.d.ts.map +1 -0
  4. package/dist/components/AppearanceSessionControls.js +152 -0
  5. package/dist/components/AppearanceSessionControls.js.map +1 -0
  6. package/dist/components/ColorModeToggle.css +42 -0
  7. package/dist/components/ColorModeToggle.d.ts +40 -0
  8. package/dist/components/ColorModeToggle.d.ts.map +1 -0
  9. package/dist/components/ColorModeToggle.js +111 -0
  10. package/dist/components/ColorModeToggle.js.map +1 -0
  11. package/dist/components/ServerThemeInjector.d.ts.map +1 -1
  12. package/dist/components/ServerThemeInjector.js +41 -14
  13. package/dist/components/ServerThemeInjector.js.map +1 -1
  14. package/dist/components/ThemeLivePreviewSync.d.ts +1 -0
  15. package/dist/components/ThemeLivePreviewSync.d.ts.map +1 -1
  16. package/dist/components/ThemeLivePreviewSync.js +1 -1
  17. package/dist/components/ThemeLivePreviewSync.js.map +1 -1
  18. package/dist/components/ThemeSwitcher.d.ts +36 -0
  19. package/dist/components/ThemeSwitcher.d.ts.map +1 -0
  20. package/dist/components/ThemeSwitcher.js +89 -0
  21. package/dist/components/ThemeSwitcher.js.map +1 -0
  22. package/dist/components/typographyPreviewUtils.d.ts.map +1 -1
  23. package/dist/components/typographyPreviewUtils.js +8 -1
  24. package/dist/components/typographyPreviewUtils.js.map +1 -1
  25. package/dist/constants/devicePreviews.d.ts +35 -0
  26. package/dist/constants/devicePreviews.d.ts.map +1 -0
  27. package/dist/constants/devicePreviews.js +43 -0
  28. package/dist/constants/devicePreviews.js.map +1 -0
  29. package/dist/constants/themeFonts.js +9 -9
  30. package/dist/constants/themeFonts.js.map +1 -1
  31. package/dist/extended-presets.d.ts +0 -4
  32. package/dist/extended-presets.d.ts.map +1 -1
  33. package/dist/extended-presets.js +1 -18
  34. package/dist/extended-presets.js.map +1 -1
  35. package/dist/extended-themes.d.ts.map +1 -1
  36. package/dist/extended-themes.js +428 -0
  37. package/dist/extended-themes.js.map +1 -1
  38. package/dist/fields/AccessibilityAuditField.d.ts +2 -0
  39. package/dist/fields/AccessibilityAuditField.d.ts.map +1 -0
  40. package/dist/fields/AccessibilityAuditField.js +303 -0
  41. package/dist/fields/AccessibilityAuditField.js.map +1 -0
  42. package/dist/fields/AppearancePreviewField.d.ts +2 -0
  43. package/dist/fields/AppearancePreviewField.d.ts.map +1 -0
  44. package/dist/fields/AppearancePreviewField.js +763 -0
  45. package/dist/fields/AppearancePreviewField.js.map +1 -0
  46. package/dist/fields/ColorPickerField.d.ts.map +1 -1
  47. package/dist/fields/ColorPickerField.js +89 -85
  48. package/dist/fields/ColorPickerField.js.map +1 -1
  49. package/dist/fields/FontSelectField.d.ts +6 -0
  50. package/dist/fields/FontSelectField.d.ts.map +1 -1
  51. package/dist/fields/FontSelectField.js +195 -122
  52. package/dist/fields/FontSelectField.js.map +1 -1
  53. package/dist/fields/PaletteGeneratorField.d.ts +7 -0
  54. package/dist/fields/PaletteGeneratorField.d.ts.map +1 -0
  55. package/dist/fields/PaletteGeneratorField.js +327 -0
  56. package/dist/fields/PaletteGeneratorField.js.map +1 -0
  57. package/dist/fields/StylePresetField.d.ts +3 -0
  58. package/dist/fields/StylePresetField.d.ts.map +1 -0
  59. package/dist/fields/StylePresetField.js +462 -0
  60. package/dist/fields/StylePresetField.js.map +1 -0
  61. package/dist/fields/ThemeColorPickerField.css +115 -35
  62. package/dist/fields/ThemeColorPickerField.d.ts.map +1 -1
  63. package/dist/fields/ThemeColorPickerField.js +204 -70
  64. package/dist/fields/ThemeColorPickerField.js.map +1 -1
  65. package/dist/fields/ThemeExportField.d.ts +6 -0
  66. package/dist/fields/ThemeExportField.d.ts.map +1 -0
  67. package/dist/fields/ThemeExportField.js +202 -0
  68. package/dist/fields/ThemeExportField.js.map +1 -0
  69. package/dist/fields/ThemePresetImportField.d.ts.map +1 -1
  70. package/dist/fields/ThemePresetImportField.js +56 -25
  71. package/dist/fields/ThemePresetImportField.js.map +1 -1
  72. package/dist/fields/ThemePreviewField.css +382 -5
  73. package/dist/fields/ThemePreviewField.d.ts.map +1 -1
  74. package/dist/fields/ThemePreviewField.js +268 -170
  75. package/dist/fields/ThemePreviewField.js.map +1 -1
  76. package/dist/fields/ThemeTokenSelectField.d.ts.map +1 -1
  77. package/dist/fields/ThemeTokenSelectField.js +278 -80
  78. package/dist/fields/ThemeTokenSelectField.js.map +1 -1
  79. package/dist/fields/colorFieldHelpers.d.ts +90 -0
  80. package/dist/fields/colorFieldHelpers.d.ts.map +1 -0
  81. package/dist/fields/colorFieldHelpers.js +133 -0
  82. package/dist/fields/colorFieldHelpers.js.map +1 -0
  83. package/dist/fields/colorModeFields.d.ts +1 -1
  84. package/dist/fields/colorModeFields.d.ts.map +1 -1
  85. package/dist/fields/colorModeFields.js +49 -7
  86. package/dist/fields/colorModeFields.js.map +1 -1
  87. package/dist/fields/themeConfigurationField.d.ts +8 -1
  88. package/dist/fields/themeConfigurationField.d.ts.map +1 -1
  89. package/dist/fields/themeConfigurationField.js +961 -29
  90. package/dist/fields/themeConfigurationField.js.map +1 -1
  91. package/dist/globals/ThemeSettings/FontPicker/FontPreview.d.ts.map +1 -1
  92. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js +8 -4
  93. package/dist/globals/ThemeSettings/FontPicker/FontPreview.js.map +1 -1
  94. package/dist/globals/ThemeSettings/FontPicker/index.d.ts.map +1 -1
  95. package/dist/globals/ThemeSettings/FontPicker/index.js +81 -26
  96. package/dist/globals/ThemeSettings/FontPicker/index.js.map +1 -1
  97. package/dist/globals/ThemeSettings/FontPicker/styles.css +98 -41
  98. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts +6 -0
  99. package/dist/globals/ThemeSettings/FontPicker/translations.d.ts.map +1 -1
  100. package/dist/globals/ThemeSettings/FontPicker/translations.js +14 -2
  101. package/dist/globals/ThemeSettings/FontPicker/translations.js.map +1 -1
  102. package/dist/globals/ThemeSettings/config.d.ts.map +1 -1
  103. package/dist/globals/ThemeSettings/config.js +76 -79
  104. package/dist/globals/ThemeSettings/config.js.map +1 -1
  105. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.d.ts.map +1 -1
  106. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js +63 -62
  107. package/dist/globals/ThemeSettings/fields/FontLoadingGuideField.js.map +1 -1
  108. package/dist/globals/ThemeSettings/fields/TypographyInfoField.d.ts.map +1 -1
  109. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js +22 -25
  110. package/dist/globals/ThemeSettings/fields/TypographyInfoField.js.map +1 -1
  111. package/dist/hooks/useAppearanceSession.d.ts +50 -0
  112. package/dist/hooks/useAppearanceSession.d.ts.map +1 -0
  113. package/dist/hooks/useAppearanceSession.js +91 -0
  114. package/dist/hooks/useAppearanceSession.js.map +1 -0
  115. package/dist/hooks/useThemeTranslations.d.ts +13 -0
  116. package/dist/hooks/useThemeTranslations.d.ts.map +1 -0
  117. package/dist/hooks/useThemeTranslations.js +22 -0
  118. package/dist/hooks/useThemeTranslations.js.map +1 -0
  119. package/dist/i18n.d.ts +53 -0
  120. package/dist/i18n.d.ts.map +1 -0
  121. package/dist/i18n.js +53 -0
  122. package/dist/i18n.js.map +1 -0
  123. package/dist/index.d.ts +10 -4
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +130 -32
  126. package/dist/index.js.map +1 -1
  127. package/dist/payload-types.d.ts +24 -0
  128. package/dist/payload-types.d.ts.map +1 -1
  129. package/dist/payload-types.js.map +1 -1
  130. package/dist/presets.d.ts +8 -0
  131. package/dist/presets.d.ts.map +1 -1
  132. package/dist/presets.js.map +1 -1
  133. package/dist/providers/Theme/index.d.ts.map +1 -1
  134. package/dist/providers/Theme/index.js +75 -11
  135. package/dist/providers/Theme/index.js.map +1 -1
  136. package/dist/providers/Theme/themeConfig.d.ts.map +1 -1
  137. package/dist/providers/Theme/themeConfig.js +104 -0
  138. package/dist/providers/Theme/themeConfig.js.map +1 -1
  139. package/dist/providers/Theme/types.d.ts +1 -1
  140. package/dist/providers/Theme/types.d.ts.map +1 -1
  141. package/dist/providers/Theme/types.js +10 -1
  142. package/dist/providers/Theme/types.js.map +1 -1
  143. package/dist/style-presets.d.ts +66 -0
  144. package/dist/style-presets.d.ts.map +1 -0
  145. package/dist/style-presets.js +556 -0
  146. package/dist/style-presets.js.map +1 -0
  147. package/dist/translations.d.ts +159 -1
  148. package/dist/translations.d.ts.map +1 -1
  149. package/dist/translations.js +310 -9
  150. package/dist/translations.js.map +1 -1
  151. package/dist/tweakcn-special-themes.d.ts.map +1 -1
  152. package/dist/tweakcn-special-themes.js +585 -9
  153. package/dist/tweakcn-special-themes.js.map +1 -1
  154. package/dist/types/css.d.js +3 -0
  155. package/dist/types/css.d.js.map +1 -0
  156. package/dist/types.d.ts +30 -0
  157. package/dist/types.d.ts.map +1 -1
  158. package/dist/types.js +1 -1
  159. package/dist/types.js.map +1 -1
  160. package/dist/utilities/getThemeSettings.d.ts.map +1 -1
  161. package/dist/utilities/getThemeSettings.js +2 -14
  162. package/dist/utilities/getThemeSettings.js.map +1 -1
  163. package/dist/utils/contrast.d.ts +63 -0
  164. package/dist/utils/contrast.d.ts.map +1 -0
  165. package/dist/utils/contrast.js +158 -0
  166. package/dist/utils/contrast.js.map +1 -0
  167. package/dist/utils/exportTokens.d.ts +30 -0
  168. package/dist/utils/exportTokens.d.ts.map +1 -0
  169. package/dist/utils/exportTokens.js +183 -0
  170. package/dist/utils/exportTokens.js.map +1 -0
  171. package/dist/utils/generatePalette.d.ts +46 -0
  172. package/dist/utils/generatePalette.d.ts.map +1 -0
  173. package/dist/utils/generatePalette.js +127 -0
  174. package/dist/utils/generatePalette.js.map +1 -0
  175. package/dist/utils/generateThemeCSS.d.ts.map +1 -1
  176. package/dist/utils/generateThemeCSS.js +2 -40
  177. package/dist/utils/generateThemeCSS.js.map +1 -1
  178. package/dist/utils/livePreviewThemeRuntime.d.ts +16 -2
  179. package/dist/utils/livePreviewThemeRuntime.d.ts.map +1 -1
  180. package/dist/utils/livePreviewThemeRuntime.js +37 -14
  181. package/dist/utils/livePreviewThemeRuntime.js.map +1 -1
  182. package/dist/utils/localizedText.d.ts +8 -0
  183. package/dist/utils/localizedText.d.ts.map +1 -0
  184. package/dist/utils/localizedText.js +21 -0
  185. package/dist/utils/localizedText.js.map +1 -0
  186. package/dist/utils/resolveThemeConfiguration.d.ts +45 -1
  187. package/dist/utils/resolveThemeConfiguration.d.ts.map +1 -1
  188. package/dist/utils/resolveThemeConfiguration.js +12 -5
  189. package/dist/utils/resolveThemeConfiguration.js.map +1 -1
  190. package/dist/utils/themeColors.d.ts +3 -0
  191. package/dist/utils/themeColors.d.ts.map +1 -1
  192. package/dist/utils/themeColors.js +30 -1
  193. package/dist/utils/themeColors.js.map +1 -1
  194. package/dist/utils/themeHtmlAttributes.d.ts +32 -12
  195. package/dist/utils/themeHtmlAttributes.d.ts.map +1 -1
  196. package/dist/utils/themeHtmlAttributes.js +38 -5
  197. package/dist/utils/themeHtmlAttributes.js.map +1 -1
  198. package/dist/utils/themeUtils.d.ts +3 -5
  199. package/dist/utils/themeUtils.d.ts.map +1 -1
  200. package/dist/utils/themeUtils.js +416 -6
  201. package/dist/utils/themeUtils.js.map +1 -1
  202. package/dist/utils/tweakcnConverter.d.ts +1 -3
  203. package/dist/utils/tweakcnConverter.d.ts.map +1 -1
  204. package/dist/utils/tweakcnConverter.js +1 -3
  205. package/dist/utils/tweakcnConverter.js.map +1 -1
  206. package/package.json +11 -5
  207. package/dist/components/ThemePreview.d.ts +0 -3
  208. package/dist/components/ThemePreview.d.ts.map +0 -1
  209. package/dist/components/ThemePreview.js +0 -321
  210. package/dist/components/ThemePreview.js.map +0 -1
  211. package/dist/plugin.d.ts +0 -2
  212. package/dist/plugin.d.ts.map +0 -1
  213. package/dist/plugin.js +0 -3
  214. package/dist/plugin.js.map +0 -1
@@ -0,0 +1,158 @@
1
+ import Color from 'color';
2
+ /**
3
+ * Best-effort conversion of any CSS colour string to hex. Uses the `color`
4
+ * package first, then a 1×1 canvas (browser only) for formats `color` can't
5
+ * parse (oklch, oklab, lch, named colours in some cases).
6
+ */ export function cssColorToHex(cssColor) {
7
+ if (!cssColor || !cssColor.trim()) return null;
8
+ try {
9
+ return Color(cssColor).hex().toString();
10
+ } catch {
11
+ /* fall through to canvas */ }
12
+ if (typeof document === 'undefined') return null;
13
+ try {
14
+ const canvas = document.createElement('canvas');
15
+ canvas.width = 1;
16
+ canvas.height = 1;
17
+ const ctx = canvas.getContext('2d');
18
+ if (!ctx) return null;
19
+ ctx.fillStyle = cssColor;
20
+ ctx.fillRect(0, 0, 1, 1);
21
+ const [r, g, b] = Array.from(ctx.getImageData(0, 0, 1, 1).data);
22
+ return '#' + [
23
+ r,
24
+ g,
25
+ b
26
+ ].map((n)=>n.toString(16).padStart(2, '0')).join('');
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * WCAG contrast ratio (1–21) between two CSS colours, or `null` if either can't
33
+ * be parsed.
34
+ */ export function getContrastRatio(a, b) {
35
+ try {
36
+ return Number(Color(a).contrast(Color(b)).toFixed(2));
37
+ } catch {
38
+ const ah = cssColorToHex(a);
39
+ const bh = cssColorToHex(b);
40
+ if (!ah || !bh) return null;
41
+ try {
42
+ return Number(Color(ah).contrast(Color(bh)).toFixed(2));
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * Map a contrast ratio to a WCAG conformance level.
50
+ * @param ratio Contrast ratio (1–21).
51
+ * @param isLargeText Use the relaxed large-text thresholds (≥18.66px bold / ≥24px).
52
+ */ export function getWcagLevel(ratio, isLargeText = false) {
53
+ if (isLargeText) {
54
+ if (ratio >= 4.5) return 'AAA';
55
+ if (ratio >= 3) return 'AA';
56
+ return 'fail';
57
+ }
58
+ if (ratio >= 7) return 'AAA';
59
+ if (ratio >= 4.5) return 'AA';
60
+ if (ratio >= 3) return 'AA Large';
61
+ return 'fail';
62
+ }
63
+ /**
64
+ * Suggest the nearest accessible variant of `foreground` (preserving hue and
65
+ * saturation, adjusting only lightness) that meets `target` contrast against
66
+ * `background`. Returns a hex string, or `null` if none is reachable / parsing
67
+ * failed.
68
+ */ export function suggestAccessibleColor(foreground, background, target = 4.5) {
69
+ try {
70
+ const bg = Color(background);
71
+ const fg = Color(foreground);
72
+ const baseLightness = fg.hsl().object().l ?? 50;
73
+ let best = null;
74
+ let bestDelta = Number.POSITIVE_INFINITY;
75
+ for(let l = 0; l <= 100; l++){
76
+ const candidate = fg.lightness(l);
77
+ const ratio = candidate.contrast(bg);
78
+ if (ratio >= target) {
79
+ const delta = Math.abs(l - baseLightness);
80
+ if (delta < bestDelta) {
81
+ bestDelta = delta;
82
+ best = candidate.hex().toString();
83
+ }
84
+ }
85
+ }
86
+ return best;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ /**
92
+ * The foreground/background token pairs that matter for readability. Each entry
93
+ * is audited against the WCAG AA normal-text threshold (4.5:1).
94
+ */ export const CONTRAST_PAIRS = [
95
+ {
96
+ foregroundKey: 'foreground',
97
+ backgroundKey: 'background',
98
+ label: 'Body text'
99
+ },
100
+ {
101
+ foregroundKey: 'mutedForeground',
102
+ backgroundKey: 'background',
103
+ label: 'Muted text'
104
+ },
105
+ {
106
+ foregroundKey: 'primaryForeground',
107
+ backgroundKey: 'primary',
108
+ label: 'Primary button'
109
+ },
110
+ {
111
+ foregroundKey: 'secondaryForeground',
112
+ backgroundKey: 'secondary',
113
+ label: 'Secondary button'
114
+ },
115
+ {
116
+ foregroundKey: 'accentForeground',
117
+ backgroundKey: 'accent',
118
+ label: 'Accent'
119
+ },
120
+ {
121
+ foregroundKey: 'cardForeground',
122
+ backgroundKey: 'card',
123
+ label: 'Card text'
124
+ },
125
+ {
126
+ foregroundKey: 'destructiveForeground',
127
+ backgroundKey: 'destructive',
128
+ label: 'Destructive'
129
+ }
130
+ ];
131
+ /**
132
+ * Audit a colour-mode palette against {@link CONTRAST_PAIRS}. Pairs whose colours
133
+ * are missing/unparseable are skipped.
134
+ */ export function auditThemePalette(colors, target = 4.5) {
135
+ const results = [];
136
+ for (const pair of CONTRAST_PAIRS){
137
+ const foreground = colors[pair.foregroundKey];
138
+ const background = colors[pair.backgroundKey];
139
+ if (!foreground || !background) continue;
140
+ const ratio = getContrastRatio(foreground, background);
141
+ if (ratio == null) continue;
142
+ const passes = ratio >= target;
143
+ results.push({
144
+ foregroundKey: pair.foregroundKey,
145
+ backgroundKey: pair.backgroundKey,
146
+ label: pair.label,
147
+ foreground,
148
+ background,
149
+ ratio,
150
+ level: getWcagLevel(ratio),
151
+ passes,
152
+ suggestion: passes ? null : suggestAccessibleColor(foreground, background, target)
153
+ });
154
+ }
155
+ return results;
156
+ }
157
+
158
+ //# sourceMappingURL=contrast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/contrast.ts"],"sourcesContent":["import Color from 'color'\n\n/**\n * WCAG contrast & accessibility helpers shared by the admin colour picker and\n * the palette accessibility audit. All functions are colour-format agnostic\n * (hex / rgb / hsl via the `color` package, with a canvas fallback for modern\n * formats such as oklch when running in the browser).\n */\n\nexport type WcagLevel = 'AAA' | 'AA' | 'AA Large' | 'fail'\n\n/**\n * Best-effort conversion of any CSS colour string to hex. Uses the `color`\n * package first, then a 1×1 canvas (browser only) for formats `color` can't\n * parse (oklch, oklab, lch, named colours in some cases).\n */\nexport function cssColorToHex(cssColor: string): string | null {\n if (!cssColor || !cssColor.trim()) return null\n try {\n return Color(cssColor).hex().toString()\n } catch {\n /* fall through to canvas */\n }\n if (typeof document === 'undefined') return null\n try {\n const canvas = document.createElement('canvas')\n canvas.width = 1\n canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return null\n ctx.fillStyle = cssColor\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = Array.from(ctx.getImageData(0, 0, 1, 1).data)\n return '#' + [r, g, b].map((n) => (n as number).toString(16).padStart(2, '0')).join('')\n } catch {\n return null\n }\n}\n\n/**\n * WCAG contrast ratio (1–21) between two CSS colours, or `null` if either can't\n * be parsed.\n */\nexport function getContrastRatio(a: string, b: string): number | null {\n try {\n return Number(Color(a).contrast(Color(b)).toFixed(2))\n } catch {\n const ah = cssColorToHex(a)\n const bh = cssColorToHex(b)\n if (!ah || !bh) return null\n try {\n return Number(Color(ah).contrast(Color(bh)).toFixed(2))\n } catch {\n return null\n }\n }\n}\n\n/**\n * Map a contrast ratio to a WCAG conformance level.\n * @param ratio Contrast ratio (1–21).\n * @param isLargeText Use the relaxed large-text thresholds (≥18.66px bold / ≥24px).\n */\nexport function getWcagLevel(ratio: number, isLargeText = false): WcagLevel {\n if (isLargeText) {\n if (ratio >= 4.5) return 'AAA'\n if (ratio >= 3) return 'AA'\n return 'fail'\n }\n if (ratio >= 7) return 'AAA'\n if (ratio >= 4.5) return 'AA'\n if (ratio >= 3) return 'AA Large'\n return 'fail'\n}\n\n/**\n * Suggest the nearest accessible variant of `foreground` (preserving hue and\n * saturation, adjusting only lightness) that meets `target` contrast against\n * `background`. Returns a hex string, or `null` if none is reachable / parsing\n * failed.\n */\nexport function suggestAccessibleColor(\n foreground: string,\n background: string,\n target = 4.5,\n): string | null {\n try {\n const bg = Color(background)\n const fg = Color(foreground)\n const baseLightness = fg.hsl().object().l ?? 50\n\n let best: string | null = null\n let bestDelta = Number.POSITIVE_INFINITY\n\n for (let l = 0; l <= 100; l++) {\n const candidate = fg.lightness(l)\n const ratio = candidate.contrast(bg)\n if (ratio >= target) {\n const delta = Math.abs(l - baseLightness)\n if (delta < bestDelta) {\n bestDelta = delta\n best = candidate.hex().toString()\n }\n }\n }\n return best\n } catch {\n return null\n }\n}\n\n/** A colour-mode object keyed by token name (e.g. `background`, `primary`). */\nexport type AuditColors = Record<string, string | null | undefined>\n\nexport interface ContrastPairResult {\n /** Token key holding the foreground colour. */\n foregroundKey: string\n /** Token key holding the background colour. */\n backgroundKey: string\n /** Human-readable description of what the pair represents. */\n label: string\n foreground: string\n background: string\n ratio: number\n level: WcagLevel\n passes: boolean\n /** Nearest accessible foreground (hex) when the pair fails AA, else null. */\n suggestion: string | null\n}\n\n/**\n * The foreground/background token pairs that matter for readability. Each entry\n * is audited against the WCAG AA normal-text threshold (4.5:1).\n */\nexport const CONTRAST_PAIRS: Array<{\n foregroundKey: string\n backgroundKey: string\n label: string\n}> = [\n { foregroundKey: 'foreground', backgroundKey: 'background', label: 'Body text' },\n { foregroundKey: 'mutedForeground', backgroundKey: 'background', label: 'Muted text' },\n { foregroundKey: 'primaryForeground', backgroundKey: 'primary', label: 'Primary button' },\n { foregroundKey: 'secondaryForeground', backgroundKey: 'secondary', label: 'Secondary button' },\n { foregroundKey: 'accentForeground', backgroundKey: 'accent', label: 'Accent' },\n { foregroundKey: 'cardForeground', backgroundKey: 'card', label: 'Card text' },\n { foregroundKey: 'destructiveForeground', backgroundKey: 'destructive', label: 'Destructive' },\n]\n\n/**\n * Audit a colour-mode palette against {@link CONTRAST_PAIRS}. Pairs whose colours\n * are missing/unparseable are skipped.\n */\nexport function auditThemePalette(colors: AuditColors, target = 4.5): ContrastPairResult[] {\n const results: ContrastPairResult[] = []\n\n for (const pair of CONTRAST_PAIRS) {\n const foreground = colors[pair.foregroundKey]\n const background = colors[pair.backgroundKey]\n if (!foreground || !background) continue\n\n const ratio = getContrastRatio(foreground, background)\n if (ratio == null) continue\n\n const passes = ratio >= target\n results.push({\n foregroundKey: pair.foregroundKey,\n backgroundKey: pair.backgroundKey,\n label: pair.label,\n foreground,\n background,\n ratio,\n level: getWcagLevel(ratio),\n passes,\n suggestion: passes ? null : suggestAccessibleColor(foreground, background, target),\n })\n }\n\n return results\n}\n"],"names":["Color","cssColorToHex","cssColor","trim","hex","toString","document","canvas","createElement","width","height","ctx","getContext","fillStyle","fillRect","r","g","b","Array","from","getImageData","data","map","n","padStart","join","getContrastRatio","a","Number","contrast","toFixed","ah","bh","getWcagLevel","ratio","isLargeText","suggestAccessibleColor","foreground","background","target","bg","fg","baseLightness","hsl","object","l","best","bestDelta","POSITIVE_INFINITY","candidate","lightness","delta","Math","abs","CONTRAST_PAIRS","foregroundKey","backgroundKey","label","auditThemePalette","colors","results","pair","passes","push","level","suggestion"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /** A minimal W3C design-tokens group node. */
2
+ type TokenLeaf = {
3
+ $value: string;
4
+ $type: 'color' | 'dimension';
5
+ };
6
+ type TokenGroup = {
7
+ [key: string]: TokenLeaf | TokenGroup;
8
+ };
9
+ /**
10
+ * Build a W3C Design Tokens document from a theme configuration. Colours are
11
+ * grouped under `color.light` / `color.dark`; border radius under `radius`.
12
+ */
13
+ export declare function generateDesignTokens(themeConfiguration: unknown): TokenGroup;
14
+ /** Pretty-printed JSON string of {@link generateDesignTokens}. */
15
+ export declare function generateDesignTokensJson(themeConfiguration: unknown): string;
16
+ /**
17
+ * Generate a Tailwind v4 `@theme inline { … }` block. The utilities reference the
18
+ * runtime CSS variables (`var(--primary)` …) so colours follow the active theme,
19
+ * light/dark mode and live preview without rebuilding Tailwind.
20
+ *
21
+ * Drop the output into your global stylesheet (after `@import "tailwindcss";`).
22
+ */
23
+ export declare function generateTailwindV4Theme(themeConfiguration: unknown): string;
24
+ /**
25
+ * Generate a Tailwind v3 `theme.extend` config fragment (as a JS string) mapping
26
+ * colours to the runtime CSS variables. For projects still on Tailwind v3.
27
+ */
28
+ export declare function generateTailwindV3Theme(themeConfiguration: unknown): string;
29
+ export {};
30
+ //# sourceMappingURL=exportTokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportTokens.d.ts","sourceRoot":"","sources":["../../src/utils/exportTokens.ts"],"names":[],"mappings":"AAyDA,8CAA8C;AAC9C,KAAK,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAAA;CAAE,CAAA;AACjE,KAAK,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAA;CAAE,CAAA;AAE3D;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,kBAAkB,EAAE,OAAO,GAAG,UAAU,CAsB5E;AAED,kEAAkE;AAClE,wBAAgB,wBAAwB,CAAC,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAE5E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAK3E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAiB3E"}
@@ -0,0 +1,183 @@
1
+ import { getBorderRadiusConfig } from '../providers/Theme/themeConfig.js';
2
+ import { resolveThemeConfiguration } from './resolveThemeConfiguration.js';
3
+ /** Colour token key → CSS variable → Tailwind utility name. */ const COLOR_TOKENS = [
4
+ {
5
+ key: 'background',
6
+ cssVar: '--background',
7
+ tw: 'background'
8
+ },
9
+ {
10
+ key: 'foreground',
11
+ cssVar: '--foreground',
12
+ tw: 'foreground'
13
+ },
14
+ {
15
+ key: 'card',
16
+ cssVar: '--card',
17
+ tw: 'card'
18
+ },
19
+ {
20
+ key: 'cardForeground',
21
+ cssVar: '--card-foreground',
22
+ tw: 'card-foreground'
23
+ },
24
+ {
25
+ key: 'popover',
26
+ cssVar: '--popover',
27
+ tw: 'popover'
28
+ },
29
+ {
30
+ key: 'popoverForeground',
31
+ cssVar: '--popover-foreground',
32
+ tw: 'popover-foreground'
33
+ },
34
+ {
35
+ key: 'primary',
36
+ cssVar: '--primary',
37
+ tw: 'primary'
38
+ },
39
+ {
40
+ key: 'primaryForeground',
41
+ cssVar: '--primary-foreground',
42
+ tw: 'primary-foreground'
43
+ },
44
+ {
45
+ key: 'secondary',
46
+ cssVar: '--secondary',
47
+ tw: 'secondary'
48
+ },
49
+ {
50
+ key: 'secondaryForeground',
51
+ cssVar: '--secondary-foreground',
52
+ tw: 'secondary-foreground'
53
+ },
54
+ {
55
+ key: 'muted',
56
+ cssVar: '--muted',
57
+ tw: 'muted'
58
+ },
59
+ {
60
+ key: 'mutedForeground',
61
+ cssVar: '--muted-foreground',
62
+ tw: 'muted-foreground'
63
+ },
64
+ {
65
+ key: 'accent',
66
+ cssVar: '--accent',
67
+ tw: 'accent'
68
+ },
69
+ {
70
+ key: 'accentForeground',
71
+ cssVar: '--accent-foreground',
72
+ tw: 'accent-foreground'
73
+ },
74
+ {
75
+ key: 'destructive',
76
+ cssVar: '--destructive',
77
+ tw: 'destructive'
78
+ },
79
+ {
80
+ key: 'destructiveForeground',
81
+ cssVar: '--destructive-foreground',
82
+ tw: 'destructive-foreground'
83
+ },
84
+ {
85
+ key: 'border',
86
+ cssVar: '--border',
87
+ tw: 'border'
88
+ },
89
+ {
90
+ key: 'input',
91
+ cssVar: '--input',
92
+ tw: 'input'
93
+ },
94
+ {
95
+ key: 'ring',
96
+ cssVar: '--ring',
97
+ tw: 'ring'
98
+ }
99
+ ];
100
+ const asColorRecord = (mode)=>{
101
+ const out = {};
102
+ if (mode && typeof mode === 'object') {
103
+ for (const { key } of COLOR_TOKENS){
104
+ const value = mode[key];
105
+ if (typeof value === 'string' && value.trim()) out[key] = value.trim();
106
+ }
107
+ }
108
+ return out;
109
+ };
110
+ const resolveRadiusValue = (borderRadius)=>{
111
+ const config = getBorderRadiusConfig(borderRadius ?? 'medium');
112
+ if (typeof config?.css === 'string') return config.css;
113
+ const record = config?.css ?? {};
114
+ return record['--radius-default'] ?? record['--radius-large'] ?? '0.5rem';
115
+ };
116
+ /**
117
+ * Build a W3C Design Tokens document from a theme configuration. Colours are
118
+ * grouped under `color.light` / `color.dark`; border radius under `radius`.
119
+ */ export function generateDesignTokens(themeConfiguration) {
120
+ const resolved = resolveThemeConfiguration(themeConfiguration);
121
+ const light = asColorRecord(resolved.lightMode);
122
+ const dark = asColorRecord(resolved.darkMode);
123
+ const toGroup = (colors)=>{
124
+ const group = {};
125
+ for (const { key } of COLOR_TOKENS){
126
+ if (colors[key]) group[key] = {
127
+ $value: colors[key],
128
+ $type: 'color'
129
+ };
130
+ }
131
+ return group;
132
+ };
133
+ return {
134
+ color: {
135
+ light: toGroup(light),
136
+ dark: toGroup(dark)
137
+ },
138
+ radius: {
139
+ base: {
140
+ $value: resolveRadiusValue(resolved.borderRadius),
141
+ $type: 'dimension'
142
+ }
143
+ }
144
+ };
145
+ }
146
+ /** Pretty-printed JSON string of {@link generateDesignTokens}. */ export function generateDesignTokensJson(themeConfiguration) {
147
+ return JSON.stringify(generateDesignTokens(themeConfiguration), null, 2);
148
+ }
149
+ /**
150
+ * Generate a Tailwind v4 `@theme inline { … }` block. The utilities reference the
151
+ * runtime CSS variables (`var(--primary)` …) so colours follow the active theme,
152
+ * light/dark mode and live preview without rebuilding Tailwind.
153
+ *
154
+ * Drop the output into your global stylesheet (after `@import "tailwindcss";`).
155
+ */ export function generateTailwindV4Theme(themeConfiguration) {
156
+ const resolved = resolveThemeConfiguration(themeConfiguration);
157
+ const lines = COLOR_TOKENS.map(({ cssVar, tw })=>` --color-${tw}: var(${cssVar});`);
158
+ lines.push(` --radius: ${resolveRadiusValue(resolved.borderRadius)};`);
159
+ return `@theme inline {\n${lines.join('\n')}\n}`;
160
+ }
161
+ /**
162
+ * Generate a Tailwind v3 `theme.extend` config fragment (as a JS string) mapping
163
+ * colours to the runtime CSS variables. For projects still on Tailwind v3.
164
+ */ export function generateTailwindV3Theme(themeConfiguration) {
165
+ const resolved = resolveThemeConfiguration(themeConfiguration);
166
+ const colorEntries = COLOR_TOKENS.map(({ cssVar, tw })=>` '${tw}': 'var(${cssVar})',`);
167
+ return [
168
+ 'module.exports = {',
169
+ ' theme: {',
170
+ ' extend: {',
171
+ ' colors: {',
172
+ colorEntries.join('\n'),
173
+ ' },',
174
+ ' borderRadius: {',
175
+ ` DEFAULT: '${resolveRadiusValue(resolved.borderRadius)}',`,
176
+ ' },',
177
+ ' },',
178
+ ' },',
179
+ '}'
180
+ ].join('\n');
181
+ }
182
+
183
+ //# sourceMappingURL=exportTokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/exportTokens.ts"],"sourcesContent":["import type { BorderRadiusPreset } from '../providers/Theme/types.js'\nimport { getBorderRadiusConfig } from '../providers/Theme/themeConfig.js'\nimport { resolveThemeConfiguration } from './resolveThemeConfiguration.js'\n\n/**\n * Export the resolved theme as portable design tokens.\n *\n * - {@link generateDesignTokens} → W3C Design Tokens JSON (Figma Tokens /\n * Style Dictionary interop).\n * - {@link generateTailwindV4Theme} → a Tailwind v4 `@theme inline { … }` block\n * that maps utilities (`bg-primary`, `text-foreground`, …) to the CSS variables\n * the plugin injects at runtime, so colours stay theme-driven.\n */\n\ntype TokenColumn = { key: string; cssVar: string; tw: string }\n\n/** Colour token key → CSS variable → Tailwind utility name. */\nconst COLOR_TOKENS: TokenColumn[] = [\n { key: 'background', cssVar: '--background', tw: 'background' },\n { key: 'foreground', cssVar: '--foreground', tw: 'foreground' },\n { key: 'card', cssVar: '--card', tw: 'card' },\n { key: 'cardForeground', cssVar: '--card-foreground', tw: 'card-foreground' },\n { key: 'popover', cssVar: '--popover', tw: 'popover' },\n { key: 'popoverForeground', cssVar: '--popover-foreground', tw: 'popover-foreground' },\n { key: 'primary', cssVar: '--primary', tw: 'primary' },\n { key: 'primaryForeground', cssVar: '--primary-foreground', tw: 'primary-foreground' },\n { key: 'secondary', cssVar: '--secondary', tw: 'secondary' },\n { key: 'secondaryForeground', cssVar: '--secondary-foreground', tw: 'secondary-foreground' },\n { key: 'muted', cssVar: '--muted', tw: 'muted' },\n { key: 'mutedForeground', cssVar: '--muted-foreground', tw: 'muted-foreground' },\n { key: 'accent', cssVar: '--accent', tw: 'accent' },\n { key: 'accentForeground', cssVar: '--accent-foreground', tw: 'accent-foreground' },\n { key: 'destructive', cssVar: '--destructive', tw: 'destructive' },\n { key: 'destructiveForeground', cssVar: '--destructive-foreground', tw: 'destructive-foreground' },\n { key: 'border', cssVar: '--border', tw: 'border' },\n { key: 'input', cssVar: '--input', tw: 'input' },\n { key: 'ring', cssVar: '--ring', tw: 'ring' },\n]\n\nconst asColorRecord = (mode: unknown): Record<string, string> => {\n const out: Record<string, string> = {}\n if (mode && typeof mode === 'object') {\n for (const { key } of COLOR_TOKENS) {\n const value = (mode as Record<string, unknown>)[key]\n if (typeof value === 'string' && value.trim()) out[key] = value.trim()\n }\n }\n return out\n}\n\nconst resolveRadiusValue = (borderRadius: string | undefined): string => {\n const config = getBorderRadiusConfig((borderRadius ?? 'medium') as BorderRadiusPreset)\n if (typeof config?.css === 'string') return config.css\n const record = (config?.css ?? {}) as Record<string, string>\n return record['--radius-default'] ?? record['--radius-large'] ?? '0.5rem'\n}\n\n/** A minimal W3C design-tokens group node. */\ntype TokenLeaf = { $value: string; $type: 'color' | 'dimension' }\ntype TokenGroup = { [key: string]: TokenLeaf | TokenGroup }\n\n/**\n * Build a W3C Design Tokens document from a theme configuration. Colours are\n * grouped under `color.light` / `color.dark`; border radius under `radius`.\n */\nexport function generateDesignTokens(themeConfiguration: unknown): TokenGroup {\n const resolved = resolveThemeConfiguration(themeConfiguration)\n const light = asColorRecord(resolved.lightMode)\n const dark = asColorRecord(resolved.darkMode)\n\n const toGroup = (colors: Record<string, string>): TokenGroup => {\n const group: TokenGroup = {}\n for (const { key } of COLOR_TOKENS) {\n if (colors[key]) group[key] = { $value: colors[key], $type: 'color' }\n }\n return group\n }\n\n return {\n color: {\n light: toGroup(light),\n dark: toGroup(dark),\n },\n radius: {\n base: { $value: resolveRadiusValue(resolved.borderRadius), $type: 'dimension' },\n },\n }\n}\n\n/** Pretty-printed JSON string of {@link generateDesignTokens}. */\nexport function generateDesignTokensJson(themeConfiguration: unknown): string {\n return JSON.stringify(generateDesignTokens(themeConfiguration), null, 2)\n}\n\n/**\n * Generate a Tailwind v4 `@theme inline { … }` block. The utilities reference the\n * runtime CSS variables (`var(--primary)` …) so colours follow the active theme,\n * light/dark mode and live preview without rebuilding Tailwind.\n *\n * Drop the output into your global stylesheet (after `@import \"tailwindcss\";`).\n */\nexport function generateTailwindV4Theme(themeConfiguration: unknown): string {\n const resolved = resolveThemeConfiguration(themeConfiguration)\n const lines = COLOR_TOKENS.map(({ cssVar, tw }) => ` --color-${tw}: var(${cssVar});`)\n lines.push(` --radius: ${resolveRadiusValue(resolved.borderRadius)};`)\n return `@theme inline {\\n${lines.join('\\n')}\\n}`\n}\n\n/**\n * Generate a Tailwind v3 `theme.extend` config fragment (as a JS string) mapping\n * colours to the runtime CSS variables. For projects still on Tailwind v3.\n */\nexport function generateTailwindV3Theme(themeConfiguration: unknown): string {\n const resolved = resolveThemeConfiguration(themeConfiguration)\n const colorEntries = COLOR_TOKENS.map(({ cssVar, tw }) => ` '${tw}': 'var(${cssVar})',`)\n return [\n 'module.exports = {',\n ' theme: {',\n ' extend: {',\n ' colors: {',\n colorEntries.join('\\n'),\n ' },',\n ' borderRadius: {',\n ` DEFAULT: '${resolveRadiusValue(resolved.borderRadius)}',`,\n ' },',\n ' },',\n ' },',\n '}',\n ].join('\\n')\n}\n"],"names":["getBorderRadiusConfig","resolveThemeConfiguration","COLOR_TOKENS","key","cssVar","tw","asColorRecord","mode","out","value","trim","resolveRadiusValue","borderRadius","config","css","record","generateDesignTokens","themeConfiguration","resolved","light","lightMode","dark","darkMode","toGroup","colors","group","$value","$type","color","radius","base","generateDesignTokensJson","JSON","stringify","generateTailwindV4Theme","lines","map","push","join","generateTailwindV3Theme","colorEntries"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Generate a complete, harmonious light + dark colour palette from a single
3
+ * brand colour using colour theory (hue preserved, lightness/saturation derived).
4
+ * Foreground colours are nudged to meet WCAG AA where needed.
5
+ *
6
+ * Output keys match the plugin's colour-mode tokens, so the result can be
7
+ * dispatched straight into `themeConfiguration.lightMode` / `.darkMode`.
8
+ */
9
+ export type GeneratedColorMode = {
10
+ background: string;
11
+ foreground: string;
12
+ card: string;
13
+ cardForeground: string;
14
+ popover: string;
15
+ popoverForeground: string;
16
+ primary: string;
17
+ primaryForeground: string;
18
+ secondary: string;
19
+ secondaryForeground: string;
20
+ muted: string;
21
+ mutedForeground: string;
22
+ accent: string;
23
+ accentForeground: string;
24
+ destructive: string;
25
+ destructiveForeground: string;
26
+ border: string;
27
+ input: string;
28
+ ring: string;
29
+ };
30
+ export type GeneratedPalette = {
31
+ lightMode: GeneratedColorMode;
32
+ darkMode: GeneratedColorMode;
33
+ };
34
+ export interface GeneratePaletteOptions {
35
+ /** Fixed destructive/danger colour. Defaults to a balanced red. */
36
+ destructive?: string;
37
+ }
38
+ export declare function generatePaletteFromColor(seed: string, options?: GeneratePaletteOptions): GeneratedPalette;
39
+ /**
40
+ * Extract a few dominant colours from an image element (browser only) via a
41
+ * downsampled canvas and coarse colour bucketing. Returns hex strings ordered by
42
+ * a saturation-weighted frequency score (so brand colours rank above greys),
43
+ * suitable as seeds for {@link generatePaletteFromColor}.
44
+ */
45
+ export declare function extractDominantColors(image: HTMLImageElement, count?: number): string[];
46
+ //# sourceMappingURL=generatePalette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generatePalette.d.ts","sourceRoot":"","sources":["../../src/utils/generatePalette.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,iBAAiB,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,kBAAkB,CAAA;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;CAC7B,CAAA;AAoBD,MAAM,WAAW,sBAAsB;IACrC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,sBAA2B,GACnC,gBAAgB,CA+DlB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAmDlF"}
@@ -0,0 +1,127 @@
1
+ import Color from 'color';
2
+ import { getContrastRatio, suggestAccessibleColor } from './contrast.js';
3
+ const clamp = (value, min, max)=>Math.min(max, Math.max(min, value));
4
+ /** Build a hex colour from HSL channels. */ const hsl = (h, s, l)=>Color(`hsl(${Math.round(h)}, ${clamp(s, 0, 100)}%, ${clamp(l, 0, 100)}%)`).hex().toString();
5
+ /** Pick black or white (whichever has higher contrast), then nudge to AA. */ const readableOn = (background)=>{
6
+ const white = getContrastRatio('#ffffff', background) ?? 0;
7
+ const black = getContrastRatio('#000000', background) ?? 0;
8
+ const base = white >= black ? '#ffffff' : '#000000';
9
+ if ((base === '#ffffff' ? white : black) >= 4.5) return base;
10
+ return suggestAccessibleColor(base, background, 4.5) ?? base;
11
+ };
12
+ export function generatePaletteFromColor(seed, options = {}) {
13
+ let h = 220;
14
+ let s = 70;
15
+ try {
16
+ const obj = Color(seed).hsl().object();
17
+ h = obj.h ?? h;
18
+ s = obj.s ?? s;
19
+ } catch {
20
+ /* keep defaults if the seed can't be parsed */ }
21
+ const destructive = options.destructive ?? hsl(0, 72, 51);
22
+ const tint = (sat)=>clamp(sat, 0, 100);
23
+ // ── Light mode ──────────────────────────────────────────────
24
+ const lightPrimary = hsl(h, tint(s), 50);
25
+ const lightMode = {
26
+ background: hsl(h, tint(s * 0.18), 99),
27
+ foreground: hsl(h, tint(s * 0.25), 12),
28
+ card: hsl(h, tint(s * 0.12), 100),
29
+ cardForeground: hsl(h, tint(s * 0.25), 12),
30
+ popover: hsl(h, tint(s * 0.12), 100),
31
+ popoverForeground: hsl(h, tint(s * 0.25), 12),
32
+ primary: lightPrimary,
33
+ primaryForeground: readableOn(lightPrimary),
34
+ secondary: hsl(h, tint(s * 0.35), 94),
35
+ secondaryForeground: hsl(h, tint(s), 22),
36
+ muted: hsl(h, tint(s * 0.22), 95),
37
+ mutedForeground: hsl(h, tint(s * 0.2), 38),
38
+ accent: hsl(h, tint(s * 0.5), 92),
39
+ accentForeground: hsl(h, tint(s), 22),
40
+ destructive,
41
+ destructiveForeground: '#ffffff',
42
+ border: hsl(h, tint(s * 0.2), 88),
43
+ input: hsl(h, tint(s * 0.2), 88),
44
+ ring: lightPrimary
45
+ };
46
+ // ── Dark mode (inverted lightness, slightly desaturated surfaces) ──
47
+ const darkPrimary = hsl(h, tint(s), 58);
48
+ const darkMode = {
49
+ background: hsl(h, tint(s * 0.25), 8),
50
+ foreground: hsl(h, tint(s * 0.1), 96),
51
+ card: hsl(h, tint(s * 0.22), 11),
52
+ cardForeground: hsl(h, tint(s * 0.1), 96),
53
+ popover: hsl(h, tint(s * 0.22), 11),
54
+ popoverForeground: hsl(h, tint(s * 0.1), 96),
55
+ primary: darkPrimary,
56
+ primaryForeground: readableOn(darkPrimary),
57
+ secondary: hsl(h, tint(s * 0.25), 18),
58
+ secondaryForeground: hsl(h, tint(s * 0.1), 94),
59
+ muted: hsl(h, tint(s * 0.2), 17),
60
+ mutedForeground: hsl(h, tint(s * 0.15), 64),
61
+ accent: hsl(h, tint(s * 0.3), 22),
62
+ accentForeground: hsl(h, tint(s * 0.1), 94),
63
+ destructive: hsl(0, 62, 50),
64
+ destructiveForeground: '#ffffff',
65
+ border: hsl(h, tint(s * 0.2), 22),
66
+ input: hsl(h, tint(s * 0.2), 22),
67
+ ring: darkPrimary
68
+ };
69
+ return {
70
+ lightMode,
71
+ darkMode
72
+ };
73
+ }
74
+ /**
75
+ * Extract a few dominant colours from an image element (browser only) via a
76
+ * downsampled canvas and coarse colour bucketing. Returns hex strings ordered by
77
+ * a saturation-weighted frequency score (so brand colours rank above greys),
78
+ * suitable as seeds for {@link generatePaletteFromColor}.
79
+ */ export function extractDominantColors(image, count = 5) {
80
+ if (typeof document === 'undefined') return [];
81
+ try {
82
+ const size = 64;
83
+ const canvas = document.createElement('canvas');
84
+ canvas.width = size;
85
+ canvas.height = size;
86
+ const ctx = canvas.getContext('2d');
87
+ if (!ctx) return [];
88
+ ctx.drawImage(image, 0, 0, size, size);
89
+ const { data } = ctx.getImageData(0, 0, size, size);
90
+ const buckets = new Map();
91
+ for(let i = 0; i < data.length; i += 4){
92
+ const a = data[i + 3];
93
+ if (a < 125) continue; // skip transparent
94
+ const r = data[i];
95
+ const g = data[i + 1];
96
+ const b = data[i + 2];
97
+ // Quantise to 5 bits per channel to group similar colours.
98
+ const key = `${r >> 5}-${g >> 5}-${b >> 5}`;
99
+ const max = Math.max(r, g, b);
100
+ const min = Math.min(r, g, b);
101
+ const saturation = max === 0 ? 0 : (max - min) / max;
102
+ const entry = buckets.get(key) ?? {
103
+ r: 0,
104
+ g: 0,
105
+ b: 0,
106
+ n: 0,
107
+ score: 0
108
+ };
109
+ entry.r += r;
110
+ entry.g += g;
111
+ entry.b += b;
112
+ entry.n += 1;
113
+ // Weight by saturation so vivid brand colours outrank near-greys.
114
+ entry.score += 1 + saturation * 4;
115
+ buckets.set(key, entry);
116
+ }
117
+ return Array.from(buckets.values()).sort((a, b)=>b.score - a.score).slice(0, count).map((e)=>Color({
118
+ r: Math.round(e.r / e.n),
119
+ g: Math.round(e.g / e.n),
120
+ b: Math.round(e.b / e.n)
121
+ }).hex().toString());
122
+ } catch {
123
+ return [];
124
+ }
125
+ }
126
+
127
+ //# sourceMappingURL=generatePalette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/generatePalette.ts"],"sourcesContent":["import Color from 'color'\nimport { getContrastRatio, suggestAccessibleColor } from './contrast.js'\n\n/**\n * Generate a complete, harmonious light + dark colour palette from a single\n * brand colour using colour theory (hue preserved, lightness/saturation derived).\n * Foreground colours are nudged to meet WCAG AA where needed.\n *\n * Output keys match the plugin's colour-mode tokens, so the result can be\n * dispatched straight into `themeConfiguration.lightMode` / `.darkMode`.\n */\n\nexport type GeneratedColorMode = {\n background: string\n foreground: string\n card: string\n cardForeground: string\n popover: string\n popoverForeground: string\n primary: string\n primaryForeground: string\n secondary: string\n secondaryForeground: string\n muted: string\n mutedForeground: string\n accent: string\n accentForeground: string\n destructive: string\n destructiveForeground: string\n border: string\n input: string\n ring: string\n}\n\nexport type GeneratedPalette = {\n lightMode: GeneratedColorMode\n darkMode: GeneratedColorMode\n}\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(max, Math.max(min, value))\n\n/** Build a hex colour from HSL channels. */\nconst hsl = (h: number, s: number, l: number): string =>\n Color(`hsl(${Math.round(h)}, ${clamp(s, 0, 100)}%, ${clamp(l, 0, 100)}%)`)\n .hex()\n .toString()\n\n/** Pick black or white (whichever has higher contrast), then nudge to AA. */\nconst readableOn = (background: string): string => {\n const white = getContrastRatio('#ffffff', background) ?? 0\n const black = getContrastRatio('#000000', background) ?? 0\n const base = white >= black ? '#ffffff' : '#000000'\n if ((base === '#ffffff' ? white : black) >= 4.5) return base\n return suggestAccessibleColor(base, background, 4.5) ?? base\n}\n\nexport interface GeneratePaletteOptions {\n /** Fixed destructive/danger colour. Defaults to a balanced red. */\n destructive?: string\n}\n\nexport function generatePaletteFromColor(\n seed: string,\n options: GeneratePaletteOptions = {},\n): GeneratedPalette {\n let h = 220\n let s = 70\n try {\n const obj = Color(seed).hsl().object()\n h = obj.h ?? h\n s = obj.s ?? s\n } catch {\n /* keep defaults if the seed can't be parsed */\n }\n\n const destructive = options.destructive ?? hsl(0, 72, 51)\n const tint = (sat: number) => clamp(sat, 0, 100)\n\n // ── Light mode ──────────────────────────────────────────────\n const lightPrimary = hsl(h, tint(s), 50)\n const lightMode: GeneratedColorMode = {\n background: hsl(h, tint(s * 0.18), 99),\n foreground: hsl(h, tint(s * 0.25), 12),\n card: hsl(h, tint(s * 0.12), 100),\n cardForeground: hsl(h, tint(s * 0.25), 12),\n popover: hsl(h, tint(s * 0.12), 100),\n popoverForeground: hsl(h, tint(s * 0.25), 12),\n primary: lightPrimary,\n primaryForeground: readableOn(lightPrimary),\n secondary: hsl(h, tint(s * 0.35), 94),\n secondaryForeground: hsl(h, tint(s), 22),\n muted: hsl(h, tint(s * 0.22), 95),\n mutedForeground: hsl(h, tint(s * 0.2), 38),\n accent: hsl(h, tint(s * 0.5), 92),\n accentForeground: hsl(h, tint(s), 22),\n destructive,\n destructiveForeground: '#ffffff',\n border: hsl(h, tint(s * 0.2), 88),\n input: hsl(h, tint(s * 0.2), 88),\n ring: lightPrimary,\n }\n\n // ── Dark mode (inverted lightness, slightly desaturated surfaces) ──\n const darkPrimary = hsl(h, tint(s), 58)\n const darkMode: GeneratedColorMode = {\n background: hsl(h, tint(s * 0.25), 8),\n foreground: hsl(h, tint(s * 0.1), 96),\n card: hsl(h, tint(s * 0.22), 11),\n cardForeground: hsl(h, tint(s * 0.1), 96),\n popover: hsl(h, tint(s * 0.22), 11),\n popoverForeground: hsl(h, tint(s * 0.1), 96),\n primary: darkPrimary,\n primaryForeground: readableOn(darkPrimary),\n secondary: hsl(h, tint(s * 0.25), 18),\n secondaryForeground: hsl(h, tint(s * 0.1), 94),\n muted: hsl(h, tint(s * 0.2), 17),\n mutedForeground: hsl(h, tint(s * 0.15), 64),\n accent: hsl(h, tint(s * 0.3), 22),\n accentForeground: hsl(h, tint(s * 0.1), 94),\n destructive: hsl(0, 62, 50),\n destructiveForeground: '#ffffff',\n border: hsl(h, tint(s * 0.2), 22),\n input: hsl(h, tint(s * 0.2), 22),\n ring: darkPrimary,\n }\n\n return { lightMode, darkMode }\n}\n\n/**\n * Extract a few dominant colours from an image element (browser only) via a\n * downsampled canvas and coarse colour bucketing. Returns hex strings ordered by\n * a saturation-weighted frequency score (so brand colours rank above greys),\n * suitable as seeds for {@link generatePaletteFromColor}.\n */\nexport function extractDominantColors(image: HTMLImageElement, count = 5): string[] {\n if (typeof document === 'undefined') return []\n try {\n const size = 64\n const canvas = document.createElement('canvas')\n canvas.width = size\n canvas.height = size\n const ctx = canvas.getContext('2d')\n if (!ctx) return []\n ctx.drawImage(image, 0, 0, size, size)\n const { data } = ctx.getImageData(0, 0, size, size)\n\n const buckets = new Map<string, { r: number; g: number; b: number; n: number; score: number }>()\n\n for (let i = 0; i < data.length; i += 4) {\n const a = data[i + 3]\n if (a < 125) continue // skip transparent\n const r = data[i]\n const g = data[i + 1]\n const b = data[i + 2]\n\n // Quantise to 5 bits per channel to group similar colours.\n const key = `${r >> 5}-${g >> 5}-${b >> 5}`\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const saturation = max === 0 ? 0 : (max - min) / max\n const entry = buckets.get(key) ?? { r: 0, g: 0, b: 0, n: 0, score: 0 }\n entry.r += r\n entry.g += g\n entry.b += b\n entry.n += 1\n // Weight by saturation so vivid brand colours outrank near-greys.\n entry.score += 1 + saturation * 4\n buckets.set(key, entry)\n }\n\n return Array.from(buckets.values())\n .sort((a, b) => b.score - a.score)\n .slice(0, count)\n .map((e) =>\n Color({\n r: Math.round(e.r / e.n),\n g: Math.round(e.g / e.n),\n b: Math.round(e.b / e.n),\n })\n .hex()\n .toString(),\n )\n } catch {\n return []\n }\n}\n"],"names":["Color","getContrastRatio","suggestAccessibleColor","clamp","value","min","max","Math","hsl","h","s","l","round","hex","toString","readableOn","background","white","black","base","generatePaletteFromColor","seed","options","obj","object","destructive","tint","sat","lightPrimary","lightMode","foreground","card","cardForeground","popover","popoverForeground","primary","primaryForeground","secondary","secondaryForeground","muted","mutedForeground","accent","accentForeground","destructiveForeground","border","input","ring","darkPrimary","darkMode","extractDominantColors","image","count","document","size","canvas","createElement","width","height","ctx","getContext","drawImage","data","getImageData","buckets","Map","i","length","a","r","g","b","key","saturation","entry","get","n","score","set","Array","from","values","sort","slice","map","e"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"generateThemeCSS.d.ts","sourceRoot":"","sources":["../../src/utils/generateThemeCSS.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGhD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAuC5D;AAuSD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAEnE"}
1
+ {"version":3,"file":"generateThemeCSS.d.ts","sourceRoot":"","sources":["../../src/utils/generateThemeCSS.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAIhD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAuC5D;AAuPD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAEnE"}