@shohojdhara/atomix 0.3.13 → 0.3.15

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 (249) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +2 -0
  3. package/build-tools/EXAMPLES.md +372 -0
  4. package/build-tools/README.md +242 -0
  5. package/build-tools/__tests__/error-handler.test.js +230 -0
  6. package/build-tools/__tests__/index.test.js +141 -0
  7. package/build-tools/__tests__/rollup-plugin.test.js +194 -0
  8. package/build-tools/__tests__/utils.test.js +161 -0
  9. package/build-tools/__tests__/vite-plugin.test.js +129 -0
  10. package/build-tools/__tests__/webpack-loader.test.js +190 -0
  11. package/build-tools/error-handler.js +308 -0
  12. package/build-tools/index.d.ts +43 -0
  13. package/build-tools/index.js +88 -0
  14. package/build-tools/package.json +67 -0
  15. package/build-tools/rollup-plugin.js +236 -0
  16. package/build-tools/types.d.ts +163 -0
  17. package/build-tools/utils.js +203 -0
  18. package/build-tools/vite-plugin.js +161 -0
  19. package/build-tools/webpack-loader.js +123 -0
  20. package/dist/atomix.css +298 -167
  21. package/dist/atomix.css.map +1 -1
  22. package/dist/atomix.min.css +3 -3
  23. package/dist/atomix.min.css.map +1 -1
  24. package/dist/build-tools/EXAMPLES.md +372 -0
  25. package/dist/build-tools/README.md +242 -0
  26. package/dist/build-tools/__tests__/error-handler.test.js +230 -0
  27. package/dist/build-tools/__tests__/index.test.js +141 -0
  28. package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
  29. package/dist/build-tools/__tests__/utils.test.js +161 -0
  30. package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
  31. package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
  32. package/dist/build-tools/error-handler.js +308 -0
  33. package/dist/build-tools/index.d.ts +43 -0
  34. package/dist/build-tools/index.js +88 -0
  35. package/dist/build-tools/package.json +67 -0
  36. package/dist/build-tools/rollup-plugin.js +236 -0
  37. package/dist/build-tools/types.d.ts +163 -0
  38. package/dist/build-tools/utils.js +203 -0
  39. package/dist/build-tools/vite-plugin.js +161 -0
  40. package/dist/build-tools/webpack-loader.js +123 -0
  41. package/dist/charts.d.ts +2 -2
  42. package/dist/charts.js +87 -58
  43. package/dist/charts.js.map +1 -1
  44. package/dist/core.d.ts +42 -12
  45. package/dist/core.js +175 -135
  46. package/dist/core.js.map +1 -1
  47. package/dist/forms.d.ts +30 -16
  48. package/dist/forms.js +146 -131
  49. package/dist/forms.js.map +1 -1
  50. package/dist/heavy.d.ts +2 -2
  51. package/dist/heavy.js +151 -118
  52. package/dist/heavy.js.map +1 -1
  53. package/dist/index.d.ts +130 -106
  54. package/dist/index.esm.js +1083 -465
  55. package/dist/index.esm.js.map +1 -1
  56. package/dist/index.js +1102 -483
  57. package/dist/index.js.map +1 -1
  58. package/dist/index.min.js +1 -1
  59. package/dist/index.min.js.map +1 -1
  60. package/dist/theme.d.ts +27 -2
  61. package/dist/theme.js +721 -108
  62. package/dist/theme.js.map +1 -1
  63. package/package.json +23 -8
  64. package/scripts/atomix-cli.js +749 -1153
  65. package/scripts/cli/__tests__/README.md +81 -0
  66. package/scripts/cli/__tests__/basic.test.js +115 -0
  67. package/scripts/cli/__tests__/component-generator.test.js +332 -0
  68. package/scripts/cli/__tests__/integration.test.js +327 -0
  69. package/scripts/cli/__tests__/test-setup.js +133 -0
  70. package/scripts/cli/__tests__/token-manager.test.js +251 -0
  71. package/scripts/cli/__tests__/utils.test.js +78 -118
  72. package/scripts/cli/component-generator.js +564 -0
  73. package/scripts/cli/dependency-checker.js +355 -0
  74. package/scripts/cli/documentation-sync.js +542 -0
  75. package/scripts/cli/interactive-init.js +129 -292
  76. package/scripts/cli/mappings.js +211 -0
  77. package/scripts/cli/migration-tools.js +95 -288
  78. package/scripts/cli/template-manager.js +105 -0
  79. package/scripts/cli/templates/README.md +123 -0
  80. package/scripts/cli/templates/common-templates.js +636 -0
  81. package/scripts/cli/templates/composable-templates.js +171 -0
  82. package/scripts/cli/templates/config-templates.js +126 -0
  83. package/scripts/cli/templates/index.js +102 -0
  84. package/scripts/cli/templates/project-templates.js +342 -0
  85. package/scripts/cli/templates/react-templates.js +331 -0
  86. package/scripts/cli/templates/scss-templates.js +155 -0
  87. package/scripts/cli/templates/storybook-templates.js +236 -0
  88. package/scripts/cli/templates/testing-templates.js +224 -0
  89. package/scripts/cli/templates/testing-utils.js +278 -0
  90. package/scripts/cli/templates/token-templates.js +447 -0
  91. package/scripts/cli/templates/types-templates.js +147 -0
  92. package/scripts/cli/templates.js +35 -0
  93. package/scripts/cli/theme-bridge.js +28 -16
  94. package/scripts/cli/token-manager.js +432 -247
  95. package/scripts/cli/utils.js +37 -26
  96. package/src/components/Accordion/Accordion.stories.tsx +369 -870
  97. package/src/components/Accordion/Accordion.test.tsx +57 -0
  98. package/src/components/Accordion/Accordion.tsx +4 -0
  99. package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
  100. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
  101. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
  102. package/src/components/AtomixGlass/glass-utils.ts +2 -2
  103. package/src/components/AtomixGlass/shader-utils.ts +5 -0
  104. package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
  105. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2965 -2861
  106. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
  107. package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
  108. package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
  109. package/src/components/AtomixGlass/stories/Playground.stories.tsx +73 -59
  110. package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
  111. package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
  112. package/src/components/Avatar/Avatar.stories.tsx +239 -27
  113. package/src/components/Badge/Badge.stories.tsx +132 -373
  114. package/src/components/Badge/Badge.test.tsx +51 -0
  115. package/src/components/Badge/Badge.tsx +20 -1
  116. package/src/components/Block/Block.stories.tsx +26 -17
  117. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
  118. package/src/components/Breadcrumb/Breadcrumb.tsx +2 -2
  119. package/src/components/Button/Button.stories.tsx +463 -1126
  120. package/src/components/Button/Button.test.tsx +107 -0
  121. package/src/components/Button/Button.tsx +50 -54
  122. package/src/components/Button/ButtonGroup.stories.tsx +373 -217
  123. package/src/components/Button/README.md +5 -0
  124. package/src/components/Callout/Callout.stories.tsx +299 -644
  125. package/src/components/Callout/Callout.test.tsx +10 -10
  126. package/src/components/Callout/Callout.tsx +7 -7
  127. package/src/components/Callout/README.md +9 -8
  128. package/src/components/Card/Card.stories.tsx +248 -68
  129. package/src/components/Card/Card.tsx +2 -2
  130. package/src/components/Chart/Chart.stories.tsx +156 -14
  131. package/src/components/Chart/Chart.tsx +1 -1
  132. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
  133. package/src/components/Countdown/Countdown.stories.tsx +115 -8
  134. package/src/components/DataTable/DataTable.stories.tsx +346 -146
  135. package/src/components/DataTable/DataTable.tsx +14 -12
  136. package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
  137. package/src/components/Dropdown/Dropdown.stories.tsx +157 -37
  138. package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
  139. package/src/components/Footer/Footer.stories.tsx +392 -328
  140. package/src/components/Form/Checkbox.stories.tsx +143 -9
  141. package/src/components/Form/Checkbox.test.tsx +63 -0
  142. package/src/components/Form/Checkbox.tsx +90 -52
  143. package/src/components/Form/Form.stories.tsx +121 -22
  144. package/src/components/Form/FormGroup.stories.tsx +128 -5
  145. package/src/components/Form/Input.stories.tsx +28 -16
  146. package/src/components/Form/Input.test.tsx +59 -0
  147. package/src/components/Form/Input.tsx +97 -95
  148. package/src/components/Form/Radio.stories.tsx +232 -97
  149. package/src/components/Form/Radio.tsx +2 -2
  150. package/src/components/Form/Select.stories.tsx +144 -12
  151. package/src/components/Form/Select.tsx +2 -2
  152. package/src/components/Form/Textarea.stories.tsx +171 -13
  153. package/src/components/Form/Textarea.test.tsx +45 -0
  154. package/src/components/Form/Textarea.tsx +88 -86
  155. package/src/components/Hero/Hero.stories.tsx +333 -32
  156. package/src/components/List/List.stories.tsx +143 -5
  157. package/src/components/Modal/Modal.stories.tsx +185 -46
  158. package/src/components/Navigation/Navbar/Navbar.stories.tsx +5 -5
  159. package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
  160. package/src/components/Navigation/README.md +1 -1
  161. package/src/components/Pagination/Pagination.stories.tsx +5 -2
  162. package/src/components/Pagination/Pagination.tsx +1 -1
  163. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -10
  164. package/src/components/Popover/Popover.stories.tsx +449 -99
  165. package/src/components/ProductReview/ProductReview.tsx +1 -1
  166. package/src/components/Progress/Progress.stories.tsx +167 -5
  167. package/src/components/Progress/Progress.tsx +46 -46
  168. package/src/components/Rating/Rating.stories.tsx +4 -4
  169. package/src/components/Rating/Rating.tsx +8 -8
  170. package/src/components/River/River.stories.tsx +1 -1
  171. package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
  172. package/src/components/Slider/Slider.stories.tsx +63 -63
  173. package/src/components/Spinner/Spinner.stories.tsx +104 -10
  174. package/src/components/Spinner/Spinner.test.tsx +35 -0
  175. package/src/components/Spinner/Spinner.tsx +9 -2
  176. package/src/components/Steps/Steps.stories.tsx +172 -43
  177. package/src/components/Tabs/Tabs.stories.tsx +136 -10
  178. package/src/components/Testimonial/Testimonial.stories.tsx +121 -4
  179. package/src/components/Todo/Todo.stories.tsx +198 -9
  180. package/src/components/Toggle/Toggle.stories.tsx +153 -43
  181. package/src/components/Toggle/Toggle.test.tsx +91 -0
  182. package/src/components/Toggle/Toggle.tsx +44 -27
  183. package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
  184. package/src/components/Tooltip/Tooltip.tsx +1 -1
  185. package/src/components/Upload/Upload.stories.tsx +113 -24
  186. package/src/layouts/Grid/Grid.stories.tsx +49 -49
  187. package/src/layouts/MasonryGrid/MasonryGrid.stories.tsx +2 -2
  188. package/src/lib/README.md +2 -2
  189. package/src/lib/__tests__/theme-tools.test.ts +193 -0
  190. package/src/lib/composables/index.ts +2 -2
  191. package/src/lib/composables/useAccordion.ts +12 -3
  192. package/src/lib/composables/useAtomixGlass.ts +28 -56
  193. package/src/lib/composables/useBreadcrumb.ts +2 -2
  194. package/src/lib/composables/useCallout.ts +7 -7
  195. package/src/lib/composables/useChartExport.ts +2 -7
  196. package/src/lib/composables/useDataTable.ts +46 -29
  197. package/src/lib/composables/useNavbar.ts +1 -1
  198. package/src/lib/constants/components.ts +10 -33
  199. package/src/lib/storybook/InteractiveDemo.tsx +113 -0
  200. package/src/lib/storybook/PreviewContainer.tsx +36 -0
  201. package/src/lib/storybook/VariantsGrid.tsx +21 -0
  202. package/src/lib/storybook/index.ts +3 -0
  203. package/src/lib/theme/core/createThemeObject.ts +9 -5
  204. package/src/lib/theme/devtools/CLI.ts +155 -0
  205. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +213 -0
  206. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +566 -0
  207. package/src/lib/theme/devtools/LiveEditor.tsx +2 -1
  208. package/src/lib/theme/devtools/index.ts +3 -0
  209. package/src/lib/theme/errors/errors.ts +8 -0
  210. package/src/lib/theme/runtime/ThemeProvider.tsx +117 -57
  211. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +305 -0
  212. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +588 -0
  213. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +264 -0
  214. package/src/lib/theme/utils/index.ts +1 -0
  215. package/src/lib/theme/utils/themeValidation.ts +501 -0
  216. package/src/lib/theme-tools.ts +32 -3
  217. package/src/lib/types/components.ts +82 -27
  218. package/src/lib/utils/__tests__/csv.test.ts +45 -0
  219. package/src/lib/utils/csv.ts +17 -0
  220. package/src/lib/utils/dataTableExport.ts +1 -10
  221. package/src/lib/utils/themeNaming.ts +1 -1
  222. package/src/styles/01-settings/_index.scss +2 -1
  223. package/src/styles/01-settings/_settings.accordion.scss +28 -7
  224. package/src/styles/01-settings/_settings.colors.scss +11 -11
  225. package/src/styles/01-settings/_settings.typography.scss +5 -5
  226. package/src/styles/02-tools/_tools.utility-api.scss +14 -0
  227. package/src/styles/06-components/_components.accordion.scss +56 -14
  228. package/src/styles/06-components/_components.callout.scss +29 -33
  229. package/src/styles/06-components/_components.checkbox.scss +23 -17
  230. package/src/styles/06-components/_index.scss +1 -1
  231. package/src/styles/99-utilities/_index.scss +2 -0
  232. package/src/styles/99-utilities/_utilities.display.scss +14 -3
  233. package/src/styles/99-utilities/_utilities.flex.scss +10 -10
  234. package/src/styles/99-utilities/_utilities.scss +3 -1
  235. package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
  236. package/src/styles/99-utilities/_utilities.text.scss +28 -8
  237. package/themes/dark-complementary/README.md +98 -0
  238. package/themes/dark-complementary/index.scss +158 -0
  239. package/themes/default-light/README.md +81 -0
  240. package/themes/default-light/index.scss +154 -0
  241. package/themes/high-contrast/README.md +105 -0
  242. package/themes/high-contrast/index.scss +172 -0
  243. package/themes/test-theme/README.md +38 -0
  244. package/themes/test-theme/index.scss +47 -0
  245. package/scripts/cli/__tests__/cli-commands.test.js +0 -204
  246. package/scripts/cli/__tests__/vitest.config.js +0 -26
  247. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
  248. package/src/lib/composables/useButton.ts +0 -93
  249. package/src/lib/composables/useCheckbox.ts +0 -70
@@ -0,0 +1,566 @@
1
+ /**
2
+ * Design Tokens Customizer Component
3
+ *
4
+ * Interactive theme customizer that allows real-time editing of DesignTokens
5
+ * with live preview and export functionality.
6
+ */
7
+
8
+ import React, { useState, useCallback, useEffect, useMemo } from 'react';
9
+ import type { DesignTokens } from '../tokens/tokens';
10
+ import { defaultTokens, createTokens } from '../tokens/tokens';
11
+ import { createTheme } from '../core/createTheme';
12
+ import { createThemeObject } from '../core/createThemeObject';
13
+ import { generateCSSVariables } from '../generators/generateCSSVariables';
14
+ import { ThemePreview } from './Preview';
15
+
16
+ /**
17
+ * Customizer props
18
+ */
19
+ export interface DesignTokensCustomizerProps {
20
+ /** Initial DesignTokens to customize */
21
+ initialTokens?: Partial<DesignTokens>;
22
+ /** Callback when tokens change */
23
+ onTokensChange?: (tokens: DesignTokens) => void;
24
+ /** CSS class name */
25
+ className?: string;
26
+ /** Inline styles */
27
+ style?: React.CSSProperties;
28
+ }
29
+
30
+ /**
31
+ * Color format type
32
+ */
33
+ type ColorFormat = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla';
34
+
35
+ /**
36
+ * Token category for organization
37
+ */
38
+ type TokenCategory = 'colors' | 'typography' | 'spacing' | 'shadows' | 'borders' | 'transitions' | 'zindex' | 'breakpoints';
39
+
40
+ /**
41
+ * Design Tokens Customizer Component
42
+ */
43
+ export const DesignTokensCustomizer: React.FC<DesignTokensCustomizerProps> = ({
44
+ initialTokens = {},
45
+ onTokensChange,
46
+ className,
47
+ style,
48
+ }) => {
49
+ // Current tokens state
50
+ const [tokens, setTokens] = useState<DesignTokens>(() => createTokens(initialTokens));
51
+ const [colorFormat, setColorFormat] = useState<ColorFormat>('hex');
52
+ const [activeCategory, setActiveCategory] = useState<TokenCategory>('colors');
53
+ const [cssPreview, setCssPreview] = useState<string>('');
54
+
55
+ // Generate CSS when tokens change
56
+ useEffect(() => {
57
+ const css = createTheme(tokens, { selector: ':root', prefix: 'atomix-preview' });
58
+ setCssPreview(css);
59
+ onTokensChange?.(tokens);
60
+ }, [tokens, onTokensChange]);
61
+
62
+ // Create theme object for preview
63
+ const previewTheme = useMemo(() => {
64
+ return createThemeObject({
65
+ palette: {
66
+ primary: { main: tokens['primary'] },
67
+ secondary: { main: tokens['secondary'] },
68
+ error: { main: tokens['error'] },
69
+ warning: { main: tokens['warning'] },
70
+ info: { main: tokens['info'] },
71
+ success: { main: tokens['success'] },
72
+ background: {
73
+ default: '#ffffff',
74
+ subtle: tokens['secondary-bg-subtle'] || '#f3f4f6',
75
+ },
76
+ text: {
77
+ primary: tokens['primary-text-emphasis'] || '#111827',
78
+ secondary: tokens['secondary-text-emphasis'] || '#374151',
79
+ disabled: tokens['disabled-text-emphasis'] || '#9ca3af',
80
+ },
81
+ },
82
+ typography: {
83
+ fontFamily: tokens['body-font-family'] || '"Roboto", sans-serif',
84
+ fontSize: parseInt(tokens['body-font-size'] || '16'),
85
+ },
86
+ spacing: (factor: number) => `${0.25 * factor}rem`, // Simplified spacing
87
+ });
88
+ }, [tokens]);
89
+
90
+ // Update token value
91
+ const updateToken = useCallback((key: keyof DesignTokens, value: string) => {
92
+ setTokens(prev => ({
93
+ ...prev,
94
+ [key]: value,
95
+ }));
96
+ }, []);
97
+
98
+ // Convert color format
99
+ const convertColorFormat = useCallback((color: string, format: ColorFormat): string => {
100
+ // Parse current color
101
+ const temp = document.createElement('div');
102
+ temp.style.color = color;
103
+ document.body.appendChild(temp);
104
+ const computed = window.getComputedStyle(temp).color;
105
+ document.body.removeChild(temp);
106
+
107
+ const rgbMatch = computed.match(/\d+/g);
108
+ if (!rgbMatch || rgbMatch.length < 3) return color;
109
+
110
+ const r = parseInt(rgbMatch[0] || '0', 10);
111
+ const g = parseInt(rgbMatch[1] || '0', 10);
112
+ const b = parseInt(rgbMatch[2] || '0', 10);
113
+ const a = rgbMatch[3] ? parseFloat(rgbMatch[3]) : 1;
114
+
115
+ switch (format) {
116
+ case 'hex':
117
+ return `#${[r, g, b].map(x => x.toString(16).padStart(2, '0')).join('')}`;
118
+ case 'rgb':
119
+ return `rgb(${r}, ${g}, ${b})`;
120
+ case 'rgba':
121
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
122
+ case 'hsl':
123
+ case 'hsla':
124
+ {
125
+ const hsl = rgbToHsl(r, g, b);
126
+ return format === 'hsl'
127
+ ? `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`
128
+ : `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${a})`;
129
+ }
130
+ default:
131
+ return color;
132
+ }
133
+ }, []);
134
+
135
+ // RGB to HSL conversion
136
+ function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
137
+ r /= 255;
138
+ g /= 255;
139
+ b /= 255;
140
+
141
+ const max = Math.max(r, g, b);
142
+ const min = Math.min(r, g, b);
143
+ let h = 0;
144
+ let s = 0;
145
+ const l = (max + min) / 2;
146
+
147
+ if (max !== min) {
148
+ const d = max - min;
149
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
150
+
151
+ switch (max) {
152
+ case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
153
+ case g: h = ((b - r) / d + 2) / 6; break;
154
+ case b: h = ((r - g) / d + 4) / 6; break;
155
+ }
156
+ }
157
+
158
+ return {
159
+ h: Math.round(h * 360),
160
+ s: Math.round(s * 100),
161
+ l: Math.round(l * 100),
162
+ };
163
+ }
164
+
165
+ // Export functions
166
+ const exportTokens = useCallback(() => {
167
+ const dataStr = JSON.stringify(tokens, null, 2);
168
+ const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
169
+ const exportFileDefaultName = 'design-tokens.json';
170
+
171
+ const linkElement = document.createElement('a');
172
+ linkElement.setAttribute('href', dataUri);
173
+ linkElement.setAttribute('download', exportFileDefaultName);
174
+ linkElement.click();
175
+ }, [tokens]);
176
+
177
+ const exportTheme = useCallback(() => {
178
+ const themeCss = createTheme(tokens);
179
+ const dataUri = 'data:text/css;charset=utf-8,' + encodeURIComponent(themeCss);
180
+ const exportFileDefaultName = 'theme.css';
181
+
182
+ const linkElement = document.createElement('a');
183
+ linkElement.setAttribute('href', dataUri);
184
+ linkElement.setAttribute('download', exportFileDefaultName);
185
+ linkElement.click();
186
+ }, [tokens]);
187
+
188
+ const copyToClipboard = useCallback(() => {
189
+ navigator.clipboard?.writeText(JSON.stringify(tokens, null, 2));
190
+ }, [tokens]);
191
+
192
+ // Reset to defaults
193
+ const resetToDefaults = useCallback(() => {
194
+ setTokens(defaultTokens);
195
+ }, []);
196
+
197
+ // Load tokens from file
198
+ const loadTokensFromFile = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
199
+ const file = event.target.files?.[0];
200
+ if (!file) return;
201
+
202
+ const reader = new FileReader();
203
+ reader.onload = (e) => {
204
+ try {
205
+ const content = e.target?.result as string;
206
+ const parsedTokens = JSON.parse(content);
207
+ const mergedTokens = createTokens(parsedTokens);
208
+ setTokens(mergedTokens);
209
+ } catch (error) {
210
+ console.error('Failed to load tokens:', error);
211
+ alert('Invalid JSON file. Please select a valid design tokens JSON file.');
212
+ }
213
+ };
214
+ reader.readAsText(file);
215
+ }, []);
216
+
217
+ // Token categories with their keys
218
+ const tokenCategories = {
219
+ colors: {
220
+ label: 'Colors',
221
+ tokens: [
222
+ // Base colors
223
+ 'primary', 'secondary', 'success', 'info', 'warning', 'error', 'light', 'dark',
224
+ // RGB versions
225
+ 'primary-rgb', 'secondary-rgb', 'success-rgb', 'info-rgb', 'warning-rgb', 'error-rgb', 'light-rgb', 'dark-rgb',
226
+ // Gray scale
227
+ 'gray-1', 'gray-2', 'gray-3', 'gray-4', 'gray-5', 'gray-6', 'gray-7', 'gray-8', 'gray-9', 'gray-10',
228
+ // Primary scale
229
+ 'primary-1', 'primary-2', 'primary-3', 'primary-4', 'primary-5', 'primary-6', 'primary-7', 'primary-8', 'primary-9', 'primary-10',
230
+ // Text emphasis
231
+ 'primary-text-emphasis', 'secondary-text-emphasis', 'tertiary-text-emphasis', 'disabled-text-emphasis',
232
+ 'invert-text-emphasis', 'brand-text-emphasis', 'error-text-emphasis', 'success-text-emphasis',
233
+ 'warning-text-emphasis', 'info-text-emphasis', 'light-text-emphasis', 'dark-text-emphasis',
234
+ // Background subtle
235
+ 'primary-bg-subtle', 'secondary-bg-subtle', 'tertiary-bg-subtle', 'invert-bg-subtle',
236
+ 'brand-bg-subtle', 'error-bg-subtle', 'success-bg-subtle', 'warning-bg-subtle', 'info-bg-subtle',
237
+ 'light-bg-subtle', 'dark-bg-subtle',
238
+ // Border subtle
239
+ 'primary-border-subtle', 'secondary-border-subtle', 'success-border-subtle', 'error-border-subtle',
240
+ 'warning-border-subtle', 'info-border-subtle', 'brand-border-subtle', 'light-border-subtle', 'dark-border-subtle',
241
+ // Hover states
242
+ 'primary-hover', 'secondary-hover', 'light-hover', 'dark-hover', 'error-hover', 'success-hover',
243
+ 'warning-hover', 'info-hover',
244
+ // Gradients
245
+ 'primary-gradient', 'secondary-gradient', 'light-gradient', 'dark-gradient', 'success-gradient',
246
+ 'info-gradient', 'warning-gradient', 'error-gradient', 'gradient',
247
+ ],
248
+ },
249
+ typography: {
250
+ label: 'Typography',
251
+ tokens: [
252
+ 'font-sans-serif', 'font-monospace', 'body-font-family', 'body-font-size', 'body-font-weight',
253
+ 'body-line-height', 'body-color', 'body-bg', 'heading-color',
254
+ 'font-size-xl', 'font-size-2xl', 'display-1',
255
+ 'font-weight-light', 'font-weight-normal', 'font-weight-medium', 'font-weight-semibold',
256
+ 'font-weight-bold', 'font-weight-heavy', 'font-weight-black',
257
+ 'line-height-base', 'line-height-sm', 'line-height-lg',
258
+ 'letter-spacing-h1', 'letter-spacing-h2', 'letter-spacing-h3', 'letter-spacing-h4',
259
+ 'letter-spacing-h5', 'letter-spacing-h6',
260
+ 'link-color', 'link-color-rgb', 'link-decoration', 'link-hover-color', 'link-hover-color-rgb',
261
+ 'highlight-bg', 'code-color',
262
+ ],
263
+ },
264
+ spacing: {
265
+ label: 'Spacing',
266
+ tokens: [
267
+ 'spacing-0', 'spacing-1', 'spacing-px-6', 'spacing-2', 'spacing-px-10', 'spacing-3', 'spacing-px-14',
268
+ 'spacing-4', 'spacing-5', 'spacing-px-22', 'spacing-6', 'spacing-7', 'spacing-px-30', 'spacing-8',
269
+ 'spacing-9', 'spacing-10', 'spacing-11', 'spacing-12', 'spacing-14', 'spacing-16', 'spacing-20',
270
+ 'spacing-24', 'spacing-28', 'spacing-32', 'spacing-36', 'spacing-40', 'spacing-44', 'spacing-48',
271
+ 'spacing-52', 'spacing-56', 'spacing-60', 'spacing-64', 'spacing-72', 'spacing-80', 'spacing-90', 'spacing-200',
272
+ ],
273
+ },
274
+ shadows: {
275
+ label: 'Shadows',
276
+ tokens: [
277
+ 'box-shadow', 'box-shadow-xs', 'box-shadow-sm', 'box-shadow-lg', 'box-shadow-xl', 'box-shadow-inset',
278
+ ],
279
+ },
280
+ borders: {
281
+ label: 'Borders',
282
+ tokens: [
283
+ 'border-width', 'border-style', 'border-color', 'border-color-translucent',
284
+ 'border-radius', 'border-radius-sm', 'border-radius-lg', 'border-radius-xl', 'border-radius-xxl',
285
+ 'border-radius-2xl', 'border-radius-3xl', 'border-radius-4xl', 'border-radius-pill',
286
+ 'focus-border-color', 'focus-ring-width', 'focus-ring-offset', 'focus-ring-opacity',
287
+ 'form-valid-color', 'form-valid-border-color', 'form-invalid-color', 'form-invalid-border-color',
288
+ ],
289
+ },
290
+ transitions: {
291
+ label: 'Transitions',
292
+ tokens: [
293
+ 'transition-duration-fast', 'transition-duration-base', 'transition-duration-slow', 'transition-duration-slower',
294
+ 'easing-base', 'easing-ease-in-out', 'easing-ease-out', 'easing-ease-in', 'easing-ease-linear',
295
+ 'transition-fast', 'transition-base', 'transition-slow',
296
+ ],
297
+ },
298
+ zindex: {
299
+ label: 'Z-Index',
300
+ tokens: [
301
+ 'z-n1', 'z-0', 'z-1', 'z-2', 'z-3', 'z-4', 'z-5', 'z-dropdown', 'z-sticky', 'z-fixed',
302
+ 'z-modal', 'z-popover', 'z-tooltip', 'z-drawer',
303
+ ],
304
+ },
305
+ breakpoints: {
306
+ label: 'Breakpoints',
307
+ tokens: [
308
+ 'breakpoint-xs', 'breakpoint-sm', 'breakpoint-md', 'breakpoint-lg', 'breakpoint-xl', 'breakpoint-xxl',
309
+ ],
310
+ },
311
+ };
312
+
313
+ return (
314
+ <div className={`design-tokens-customizer ${className || ''}`} style={style}>
315
+ <div className="customizer-header">
316
+ <h2>Interactive Theme Customizer</h2>
317
+ <div className="customizer-controls">
318
+ <select
319
+ value={colorFormat}
320
+ onChange={(e) => setColorFormat(e.target.value as ColorFormat)}
321
+ >
322
+ <option value="hex">HEX</option>
323
+ <option value="rgb">RGB</option>
324
+ <option value="rgba">RGBA</option>
325
+ <option value="hsl">HSL</option>
326
+ <option value="hsla">HSLA</option>
327
+ </select>
328
+ <button onClick={resetToDefaults}>Reset to Defaults</button>
329
+ <label className="file-input-button">
330
+ Load Tokens
331
+ <input
332
+ type="file"
333
+ accept=".json"
334
+ onChange={loadTokensFromFile}
335
+ style={{ display: 'none' }}
336
+ />
337
+ </label>
338
+ <button onClick={copyToClipboard}>Copy Tokens</button>
339
+ <button onClick={exportTokens}>Export Tokens</button>
340
+ <button onClick={exportTheme}>Export Theme CSS</button>
341
+ </div>
342
+ </div>
343
+
344
+ <div className="customizer-content">
345
+ <div className="customizer-sidebar">
346
+ {Object.entries(tokenCategories).map(([key, category]) => (
347
+ <button
348
+ key={key}
349
+ className={`category-button ${activeCategory === key ? 'active' : ''}`}
350
+ onClick={() => setActiveCategory(key as TokenCategory)}
351
+ >
352
+ {category.label}
353
+ </button>
354
+ ))}
355
+ </div>
356
+
357
+ <div className="customizer-editor">
358
+ <h3>{tokenCategories[activeCategory].label}</h3>
359
+ <div className="tokens-grid">
360
+ {tokenCategories[activeCategory].tokens.map((tokenKey) => {
361
+ const value = tokens[tokenKey as keyof DesignTokens] || '';
362
+ const isColor = tokenKey.includes('color') || tokenKey.includes('bg') || tokenKey.includes('gradient') ||
363
+ ['primary', 'secondary', 'success', 'info', 'warning', 'error', 'light', 'dark'].includes(tokenKey) ||
364
+ tokenKey.match(/^(gray|primary|red|green|blue|yellow)-\d+$/);
365
+
366
+ return (
367
+ <div key={tokenKey} className="token-item">
368
+ <label>{tokenKey}</label>
369
+ {isColor ? (
370
+ <div className="color-input-group">
371
+ <input
372
+ type="color"
373
+ value={value.startsWith('#') ? value : convertColorFormat(value, 'hex')}
374
+ onChange={(e) => updateToken(tokenKey as keyof DesignTokens, e.target.value)}
375
+ />
376
+ <input
377
+ type="text"
378
+ value={convertColorFormat(value, colorFormat)}
379
+ onChange={(e) => {
380
+ const converted = convertColorFormat(e.target.value, 'hex');
381
+ updateToken(tokenKey as keyof DesignTokens, converted);
382
+ }}
383
+ />
384
+ </div>
385
+ ) : (
386
+ <input
387
+ type="text"
388
+ value={value}
389
+ onChange={(e) => updateToken(tokenKey as keyof DesignTokens, e.target.value)}
390
+ />
391
+ )}
392
+ </div>
393
+ );
394
+ })}
395
+ </div>
396
+ </div>
397
+
398
+ <div className="customizer-preview">
399
+ <h3>Live Preview</h3>
400
+ <style>{cssPreview}</style>
401
+ <ThemePreview
402
+ theme={previewTheme}
403
+ showDetails={false}
404
+ showPalette={true}
405
+ showTypography={true}
406
+ showSpacing={true}
407
+ />
408
+ </div>
409
+ </div>
410
+
411
+ <style>{`
412
+ .design-tokens-customizer {
413
+ display: flex;
414
+ flex-direction: column;
415
+ height: 100vh;
416
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
417
+ }
418
+
419
+ .customizer-header {
420
+ display: flex;
421
+ justify-content: space-between;
422
+ align-items: center;
423
+ padding: 16px 24px;
424
+ border-bottom: 1px solid #e0e0e0;
425
+ background: #f5f5f5;
426
+ }
427
+
428
+ .customizer-header h2 {
429
+ margin: 0;
430
+ font-size: 24px;
431
+ color: #333;
432
+ }
433
+
434
+ .customizer-controls {
435
+ display: flex;
436
+ gap: 8px;
437
+ align-items: center;
438
+ }
439
+
440
+ .customizer-controls select,
441
+ .customizer-controls button {
442
+ padding: 8px 12px;
443
+ border: 1px solid #e0e0e0;
444
+ border-radius: 4px;
445
+ background: white;
446
+ cursor: pointer;
447
+ }
448
+
449
+ .customizer-controls button:hover,
450
+ .file-input-button:hover {
451
+ background: #f0f0f0;
452
+ }
453
+
454
+ .file-input-button {
455
+ padding: 8px 12px;
456
+ border: 1px solid #e0e0e0;
457
+ border-radius: 4px;
458
+ background: white;
459
+ cursor: pointer;
460
+ font-size: 14px;
461
+ display: inline-block;
462
+ }
463
+
464
+ .customizer-content {
465
+ display: flex;
466
+ flex: 1;
467
+ overflow: hidden;
468
+ }
469
+
470
+ .customizer-sidebar {
471
+ width: 200px;
472
+ border-right: 1px solid #e0e0e0;
473
+ padding: 16px;
474
+ background: #fafafa;
475
+ overflow-y: auto;
476
+ }
477
+
478
+ .category-button {
479
+ display: block;
480
+ width: 100%;
481
+ padding: 12px;
482
+ margin-bottom: 8px;
483
+ border: none;
484
+ background: white;
485
+ border-radius: 4px;
486
+ cursor: pointer;
487
+ text-align: left;
488
+ font-size: 14px;
489
+ }
490
+
491
+ .category-button:hover,
492
+ .category-button.active {
493
+ background: #e0e0e0;
494
+ }
495
+
496
+ .customizer-editor {
497
+ flex: 1;
498
+ padding: 24px;
499
+ overflow-y: auto;
500
+ }
501
+
502
+ .customizer-editor h3 {
503
+ margin-top: 0;
504
+ margin-bottom: 16px;
505
+ color: #333;
506
+ }
507
+
508
+ .tokens-grid {
509
+ display: grid;
510
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
511
+ gap: 16px;
512
+ }
513
+
514
+ .token-item {
515
+ display: flex;
516
+ flex-direction: column;
517
+ gap: 8px;
518
+ }
519
+
520
+ .token-item label {
521
+ font-size: 14px;
522
+ font-weight: 500;
523
+ color: #666;
524
+ }
525
+
526
+ .color-input-group {
527
+ display: flex;
528
+ gap: 8px;
529
+ align-items: center;
530
+ }
531
+
532
+ .color-input-group input[type="color"] {
533
+ width: 50px;
534
+ height: 40px;
535
+ border: 1px solid #e0e0e0;
536
+ border-radius: 4px;
537
+ cursor: pointer;
538
+ }
539
+
540
+ .color-input-group input[type="text"],
541
+ .token-item input[type="text"] {
542
+ flex: 1;
543
+ padding: 8px 12px;
544
+ border: 1px solid #e0e0e0;
545
+ border-radius: 4px;
546
+ font-size: 14px;
547
+ font-family: 'Monaco', 'Menlo', monospace;
548
+ }
549
+
550
+ .customizer-preview {
551
+ width: 400px;
552
+ border-left: 1px solid #e0e0e0;
553
+ padding: 24px;
554
+ overflow-y: auto;
555
+ background: #fafafa;
556
+ }
557
+
558
+ .customizer-preview h3 {
559
+ margin-top: 0;
560
+ margin-bottom: 16px;
561
+ color: #333;
562
+ }
563
+ `}</style>
564
+ </div>
565
+ );
566
+ };
@@ -68,12 +68,13 @@ function convertColorFormat(color: string, format: ColorFormat): string {
68
68
  case 'rgba':
69
69
  return `rgba(${r}, ${g}, ${b}, ${a})`;
70
70
  case 'hsl':
71
- case 'hsla':
71
+ case 'hsla': {
72
72
  // Convert RGB to HSL (simplified)
73
73
  const hsl = rgbToHsl(r, g, b);
74
74
  return format === 'hsl'
75
75
  ? `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`
76
76
  : `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${a})`;
77
+ }
77
78
  default:
78
79
  return color;
79
80
  }
@@ -17,6 +17,9 @@ export type { ThemeComparatorProps } from './Comparator';
17
17
  export { ThemeLiveEditor } from './LiveEditor';
18
18
  export type { ThemeLiveEditorProps } from './LiveEditor';
19
19
 
20
+ export { DesignTokensCustomizer } from './DesignTokensCustomizer';
21
+ export type { DesignTokensCustomizerProps } from './DesignTokensCustomizer';
22
+
20
23
  // Validator (devtools only)
21
24
  export { ThemeValidator } from './ThemeValidator';
22
25
  export type {
@@ -29,6 +29,14 @@ export enum ThemeErrorCode {
29
29
  INVALID_THEME_NAME = 'INVALID_THEME_NAME',
30
30
  /** CSS injection failed */
31
31
  CSS_INJECTION_FAILED = 'CSS_INJECTION_FAILED',
32
+ /** Invalid color format */
33
+ INVALID_COLOR_FORMAT = 'INVALID_COLOR_FORMAT',
34
+ /** Missing required token */
35
+ MISSING_REQUIRED_TOKEN = 'MISSING_REQUIRED_TOKEN',
36
+ /** Accessibility contrast violation */
37
+ CONTRAST_VIOLATION = 'CONTRAST_VIOLATION',
38
+ /** Invalid token type */
39
+ INVALID_TOKEN_TYPE = 'INVALID_TOKEN_TYPE',
32
40
  /** Unknown error */
33
41
  UNKNOWN_ERROR = 'UNKNOWN_ERROR',
34
42
  }