@shohojdhara/atomix 0.3.15 → 0.4.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 (245) hide show
  1. package/build-tools/index.d.ts +31 -30
  2. package/build-tools/package.json +4 -21
  3. package/dist/atomix.css +20924 -2611
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +76 -2
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/index.d.ts +31 -30
  8. package/dist/build-tools/package.json +4 -21
  9. package/dist/charts.js.map +1 -1
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js.map +1 -1
  12. package/dist/heavy.js.map +1 -1
  13. package/dist/index.d.ts +144 -18
  14. package/dist/index.esm.js +110 -55
  15. package/dist/index.esm.js.map +1 -1
  16. package/dist/index.js +110 -55
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.min.js +1 -1
  19. package/dist/index.min.js.map +1 -1
  20. package/dist/layout.js.map +1 -1
  21. package/dist/theme.d.ts +9 -9
  22. package/dist/theme.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/components/Accordion/Accordion.stories.tsx +32 -23
  25. package/src/components/Accordion/Accordion.test.tsx +70 -50
  26. package/src/components/Accordion/Accordion.tsx +99 -94
  27. package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
  28. package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
  29. package/src/components/AtomixGlass/glass-utils.ts +4 -3
  30. package/src/components/AtomixGlass/shader-utils.ts +128 -52
  31. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1 -1
  32. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +1 -1
  33. package/src/components/Avatar/Avatar.stories.tsx +45 -62
  34. package/src/components/Avatar/Avatar.tsx +58 -56
  35. package/src/components/Badge/Badge.stories.tsx +20 -9
  36. package/src/components/Badge/Badge.test.tsx +41 -41
  37. package/src/components/Badge/Badge.tsx +64 -62
  38. package/src/components/Block/Block.stories.tsx +14 -4
  39. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +9 -8
  40. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
  41. package/src/components/Button/Button.stories.tsx +13 -22
  42. package/src/components/Button/Button.test.tsx +97 -81
  43. package/src/components/Button/Button.tsx +46 -14
  44. package/src/components/Button/ButtonGroup.stories.tsx +37 -32
  45. package/src/components/Button/ButtonGroup.tsx +4 -15
  46. package/src/components/Callout/Callout.stories.tsx +109 -16
  47. package/src/components/Card/Card.stories.tsx +67 -36
  48. package/src/components/Card/Card.tsx +30 -14
  49. package/src/components/Chart/AreaChart.tsx +1 -1
  50. package/src/components/Chart/CandlestickChart.tsx +23 -16
  51. package/src/components/Chart/Chart.stories.tsx +4 -9
  52. package/src/components/Chart/Chart.tsx +40 -44
  53. package/src/components/Chart/ChartRenderer.tsx +39 -12
  54. package/src/components/Chart/ChartToolbar.tsx +21 -5
  55. package/src/components/Chart/DonutChart.tsx +1 -1
  56. package/src/components/Chart/FunnelChart.tsx +4 -1
  57. package/src/components/Chart/GaugeChart.tsx +3 -1
  58. package/src/components/Chart/HeatmapChart.tsx +50 -37
  59. package/src/components/Chart/LineChart.tsx +3 -2
  60. package/src/components/Chart/MultiAxisChart.tsx +24 -16
  61. package/src/components/Chart/RadarChart.tsx +19 -17
  62. package/src/components/Chart/ScatterChart.tsx +29 -21
  63. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -2
  64. package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
  65. package/src/components/Countdown/Countdown.stories.tsx +7 -7
  66. package/src/components/DataTable/DataTable.stories.tsx +43 -38
  67. package/src/components/DataTable/DataTable.test.tsx +26 -148
  68. package/src/components/DataTable/DataTable.tsx +485 -456
  69. package/src/components/DatePicker/DatePicker.stories.tsx +32 -47
  70. package/src/components/DatePicker/DatePicker.tsx +31 -26
  71. package/src/components/Dropdown/Dropdown.stories.tsx +2 -5
  72. package/src/components/Dropdown/Dropdown.tsx +313 -299
  73. package/src/components/EdgePanel/EdgePanel.stories.tsx +6 -19
  74. package/src/components/EdgePanel/EdgePanel.tsx +1 -3
  75. package/src/components/Footer/Footer.stories.tsx +21 -16
  76. package/src/components/Footer/Footer.tsx +130 -128
  77. package/src/components/Footer/FooterLink.tsx +2 -2
  78. package/src/components/Form/Checkbox.test.tsx +49 -49
  79. package/src/components/Form/Checkbox.tsx +108 -100
  80. package/src/components/Form/Form.stories.tsx +2 -10
  81. package/src/components/Form/Input.stories.tsx +22 -39
  82. package/src/components/Form/Input.test.tsx +38 -44
  83. package/src/components/Form/Radio.stories.tsx +6 -12
  84. package/src/components/Form/Radio.tsx +68 -66
  85. package/src/components/Form/Select.tsx +184 -182
  86. package/src/components/Form/Textarea.test.tsx +27 -32
  87. package/src/components/Hero/Hero.stories.tsx +56 -23
  88. package/src/components/Hero/Hero.tsx +201 -55
  89. package/src/components/Icon/index.ts +7 -1
  90. package/src/components/List/List.tsx +19 -23
  91. package/src/components/Modal/Modal.stories.tsx +2 -1
  92. package/src/components/Modal/Modal.tsx +130 -127
  93. package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
  94. package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
  95. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
  96. package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
  97. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
  98. package/src/components/Pagination/Pagination.stories.tsx +7 -4
  99. package/src/components/Pagination/Pagination.tsx +199 -202
  100. package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
  101. package/src/components/Popover/Popover.stories.tsx +99 -192
  102. package/src/components/Popover/Popover.tsx +41 -37
  103. package/src/components/Progress/Progress.stories.tsx +35 -44
  104. package/src/components/River/River.stories.tsx +2 -1
  105. package/src/components/SectionIntro/SectionIntro.stories.tsx +71 -71
  106. package/src/components/Slider/Slider.stories.tsx +12 -4
  107. package/src/components/Spinner/Spinner.stories.tsx +3 -1
  108. package/src/components/Spinner/Spinner.test.tsx +23 -23
  109. package/src/components/Spinner/Spinner.tsx +43 -46
  110. package/src/components/Steps/Steps.stories.tsx +8 -6
  111. package/src/components/Tabs/Tabs.stories.tsx +12 -9
  112. package/src/components/Tabs/Tabs.tsx +74 -72
  113. package/src/components/Toggle/Toggle.stories.tsx +27 -13
  114. package/src/components/Toggle/Toggle.test.tsx +65 -70
  115. package/src/components/Toggle/Toggle.tsx +4 -1
  116. package/src/components/Tooltip/Tooltip.stories.tsx +24 -20
  117. package/src/components/Tooltip/Tooltip.tsx +104 -106
  118. package/src/components/Upload/Upload.stories.tsx +129 -127
  119. package/src/components/Upload/Upload.tsx +287 -283
  120. package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
  121. package/src/components/index.ts +13 -2
  122. package/src/layouts/Grid/Grid.stories.tsx +9 -3
  123. package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
  124. package/src/lib/__tests__/theme-tools.test.ts +32 -6
  125. package/src/lib/composables/shared-mouse-tracker.ts +13 -14
  126. package/src/lib/composables/useAtomixGlass.ts +106 -49
  127. package/src/lib/composables/useChartExport.ts +1 -1
  128. package/src/lib/composables/useDataTable.ts +29 -17
  129. package/src/lib/composables/useHero.ts +58 -14
  130. package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
  131. package/src/lib/composables/useInput.ts +10 -8
  132. package/src/lib/composables/useSideMenu.ts +6 -5
  133. package/src/lib/composables/useTooltip.ts +1 -2
  134. package/src/lib/composables/useVideoPlayer.ts +44 -35
  135. package/src/lib/config/index.ts +154 -154
  136. package/src/lib/constants/cssVariables.ts +29 -29
  137. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
  138. package/src/lib/hooks/index.ts +1 -1
  139. package/src/lib/hooks/useComponentCustomization.ts +11 -17
  140. package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
  141. package/src/lib/patterns/__tests__/slots.test.ts +1 -1
  142. package/src/lib/patterns/index.ts +1 -1
  143. package/src/lib/patterns/slots.tsx +8 -13
  144. package/src/lib/storybook/InteractiveDemo.tsx +13 -18
  145. package/src/lib/storybook/PreviewContainer.tsx +1 -1
  146. package/src/lib/storybook/VariantsGrid.tsx +3 -7
  147. package/src/lib/storybook/index.ts +1 -1
  148. package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
  149. package/src/lib/theme/adapters/index.ts +3 -9
  150. package/src/lib/theme/adapters/themeAdapter.ts +41 -26
  151. package/src/lib/theme/config/index.ts +1 -1
  152. package/src/lib/theme/config/types.ts +2 -2
  153. package/src/lib/theme/config/validator.ts +10 -5
  154. package/src/lib/theme/constants/constants.ts +2 -2
  155. package/src/lib/theme/constants/index.ts +1 -2
  156. package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
  157. package/src/lib/theme/core/composeTheme.ts +32 -26
  158. package/src/lib/theme/core/createTheme.ts +1 -1
  159. package/src/lib/theme/core/createThemeObject.ts +308 -301
  160. package/src/lib/theme/core/index.ts +3 -3
  161. package/src/lib/theme/devtools/CLI.ts +106 -104
  162. package/src/lib/theme/devtools/Comparator.tsx +50 -32
  163. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
  164. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
  165. package/src/lib/theme/devtools/Inspector.tsx +75 -60
  166. package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
  167. package/src/lib/theme/devtools/Preview.tsx +150 -106
  168. package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
  169. package/src/lib/theme/devtools/index.ts +3 -9
  170. package/src/lib/theme/devtools/useHistory.ts +23 -21
  171. package/src/lib/theme/errors/errors.ts +12 -11
  172. package/src/lib/theme/errors/index.ts +2 -7
  173. package/src/lib/theme/generators/generateCSS.ts +9 -13
  174. package/src/lib/theme/generators/generateCSSNested.ts +1 -6
  175. package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
  176. package/src/lib/theme/generators/index.ts +1 -4
  177. package/src/lib/theme/i18n/index.ts +1 -1
  178. package/src/lib/theme/i18n/rtl.ts +13 -13
  179. package/src/lib/theme/index.ts +7 -16
  180. package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
  181. package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
  182. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
  183. package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
  184. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
  185. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
  186. package/src/lib/theme/runtime/index.ts +2 -5
  187. package/src/lib/theme/runtime/useTheme.ts +18 -18
  188. package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
  189. package/src/lib/theme/test/testTheme.ts +15 -16
  190. package/src/lib/theme/tokens/index.ts +2 -7
  191. package/src/lib/theme/tokens/tokens.ts +25 -24
  192. package/src/lib/theme/types.ts +428 -411
  193. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
  194. package/src/lib/theme/utils/componentTheming.ts +18 -18
  195. package/src/lib/theme/utils/domUtils.ts +277 -289
  196. package/src/lib/theme/utils/index.ts +1 -2
  197. package/src/lib/theme/utils/injectCSS.ts +10 -14
  198. package/src/lib/theme/utils/naming.ts +20 -16
  199. package/src/lib/theme/utils/themeHelpers.ts +10 -12
  200. package/src/lib/theme/utils/themeUtils.ts +85 -86
  201. package/src/lib/theme/utils/themeValidation.ts +82 -33
  202. package/src/lib/theme-tools.ts +8 -6
  203. package/src/lib/types/components.ts +172 -71
  204. package/src/lib/types/partProps.ts +1 -1
  205. package/src/lib/utils/__tests__/csv.test.ts +1 -1
  206. package/src/lib/utils/componentUtils.ts +8 -12
  207. package/src/lib/utils/csv.ts +3 -1
  208. package/src/lib/utils/dataTableExport.ts +1 -5
  209. package/src/lib/utils/fontPreloader.ts +10 -19
  210. package/src/lib/utils/icons.ts +4 -1
  211. package/src/lib/utils/index.ts +2 -6
  212. package/src/lib/utils/memoryMonitor.ts +10 -8
  213. package/src/lib/utils/themeNaming.ts +2 -2
  214. package/src/styles/01-settings/_index.scss +0 -1
  215. package/src/styles/01-settings/_settings.colors.scss +8 -8
  216. package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
  217. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  218. package/src/styles/01-settings/_settings.spacing.scss +3 -4
  219. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  220. package/src/styles/01-settings/_settings.typography.scss +1 -1
  221. package/src/styles/02-tools/_tools.button.scss +51 -21
  222. package/src/styles/02-tools/_tools.utility-api.scss +30 -18
  223. package/src/styles/03-generic/_generic.root.scss +4 -3
  224. package/src/styles/06-components/_components.atomix-glass.scss +13 -9
  225. package/src/styles/06-components/_components.button.scss +16 -4
  226. package/src/styles/06-components/_components.callout.scss +27 -21
  227. package/src/styles/06-components/_components.card.scss +5 -14
  228. package/src/styles/06-components/_components.chart.scss +22 -19
  229. package/src/styles/06-components/_components.checkbox.scss +3 -1
  230. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
  231. package/src/styles/06-components/_components.edge-panel.scss +9 -2
  232. package/src/styles/06-components/_components.footer.scss +1 -1
  233. package/src/styles/06-components/_components.side-menu.scss +5 -5
  234. package/src/styles/06-components/_components.toggle.scss +18 -0
  235. package/src/styles/06-components/_index.scss +1 -1
  236. package/src/styles/06-components/old.chart.styles.scss +0 -2
  237. package/src/styles/99-utilities/_utilities.border.scss +69 -27
  238. package/src/styles/99-utilities/_utilities.display.scss +1 -1
  239. package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
  240. package/src/styles/99-utilities/_utilities.position.scss +16 -9
  241. package/src/styles/99-utilities/_utilities.scss +1 -1
  242. package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
  243. package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
  244. package/src/styles/99-utilities/_utilities.text-gradient.scss +30 -30
  245. package/src/styles/99-utilities/_utilities.text.scss +67 -46
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * CSS Variable Generator
3
- *
3
+ *
4
4
  * Generates CSS custom properties from theme objects and injects them into the DOM.
5
- *
5
+ *
6
6
  * **Token Naming Alignment:**
7
7
  * This generator produces CSS variables that match the SCSS token naming pattern exactly:
8
8
  * - Colors: --atomix-primary, --atomix-primary-1 through --atomix-primary-10
9
9
  * - Spacing: --atomix-spacing-1, --atomix-spacing-4, etc.
10
10
  * - Typography: --atomix-font-size-base, --atomix-font-weight-normal, etc.
11
11
  * - Shadows: --atomix-box-shadow, --atomix-box-shadow-sm, etc.
12
- *
12
+ *
13
13
  * All tokens follow the flat structure pattern used in SCSS (not nested like --atomix-palette-primary-main).
14
14
  * This ensures compatibility between SCSS themes and JavaScript themes.
15
- *
15
+ *
16
16
  * @see src/styles/03-generic/_generic.root.scss for SCSS token definitions
17
17
  */
18
18
 
@@ -28,14 +28,14 @@ import { hexToRgb, alpha, lighten, darken, emphasize } from '../utils/themeUtils
28
28
  * Options for CSS variable generation
29
29
  */
30
30
  export interface GenerateCSSVariablesOptions {
31
- /** CSS selector for the variables (default: ':root') */
32
- selector?: string;
33
- /** Whether to inject the CSS into the DOM */
34
- inject?: boolean;
35
- /** ID for the injected style element */
36
- styleId?: string;
37
- /** Prefix for CSS variables (default: 'atomix') */
38
- prefix?: string;
31
+ /** CSS selector for the variables (default: ':root') */
32
+ selector?: string;
33
+ /** Whether to inject the CSS into the DOM */
34
+ inject?: boolean;
35
+ /** ID for the injected style element */
36
+ styleId?: string;
37
+ /** Prefix for CSS variables (default: 'atomix') */
38
+ prefix?: string;
39
39
  }
40
40
 
41
41
  /**
@@ -43,75 +43,79 @@ export interface GenerateCSSVariablesOptions {
43
43
  * Uses iterative approach for better performance with large objects
44
44
  */
45
45
  function flattenObject(
46
- obj: Record<string, any>,
47
- prefix: string = '',
48
- result: Record<string, string> = {}
46
+ obj: Record<string, any>,
47
+ prefix: string = '',
48
+ result: Record<string, string> = {}
49
49
  ): Record<string, string> {
50
- // Use iterative approach with stack to avoid deep recursion
51
- const stack: Array<{ obj: Record<string, any>; prefix: string }> = [{ obj, prefix }];
52
-
53
- while (stack.length > 0) {
54
- const { obj: currentObj, prefix: currentPrefix } = stack.pop()!;
55
-
56
- for (const key in currentObj) {
57
- if (!Object.prototype.hasOwnProperty.call(currentObj, key)) continue;
58
-
59
- const value = currentObj[key];
60
- const newKey = currentPrefix ? `${currentPrefix}-${key}` : key;
61
-
62
- if (value && typeof value === 'object' && !Array.isArray(value)) {
63
- // Skip special objects like functions
64
- if (typeof value === 'function') continue;
65
-
66
- // Add to stack for iterative processing
67
- stack.push({ obj: value, prefix: newKey });
68
- } else if (typeof value === 'string' || typeof value === 'number') {
69
- // Convert camelCase to kebab-case
70
- const kebabKey = newKey.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
71
- result[kebabKey] = String(value);
72
- }
73
- }
50
+ // Use iterative approach with stack to avoid deep recursion
51
+ const stack: Array<{ obj: Record<string, any>; prefix: string }> = [{ obj, prefix }];
52
+
53
+ while (stack.length > 0) {
54
+ const { obj: currentObj, prefix: currentPrefix } = stack.pop()!;
55
+
56
+ for (const key in currentObj) {
57
+ if (!Object.prototype.hasOwnProperty.call(currentObj, key)) continue;
58
+
59
+ const value = currentObj[key];
60
+ const newKey = currentPrefix ? `${currentPrefix}-${key}` : key;
61
+
62
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
63
+ // Skip special objects like functions
64
+ if (typeof value === 'function') continue;
65
+
66
+ // Add to stack for iterative processing
67
+ stack.push({ obj: value, prefix: newKey });
68
+ } else if (typeof value === 'string' || typeof value === 'number') {
69
+ // Convert camelCase to kebab-case
70
+ const kebabKey = newKey.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
71
+ result[kebabKey] = String(value);
72
+ }
74
73
  }
74
+ }
75
75
 
76
- return result;
76
+ return result;
77
77
  }
78
78
 
79
79
  /**
80
80
  * Generate a color scale from a base color (1-10 steps)
81
81
  * Creates lighter to darker variations
82
82
  */
83
- function generateColorScale(baseColor: string, prefix: string, colorName: string): Record<string, string> {
84
- const vars: Record<string, string> = {};
85
- const rgb = hexToRgb(baseColor);
86
- if (!rgb) return vars;
87
-
88
- // Generate 10-step scale
89
- // Steps 1-5: lighter variations
90
- // Step 6: base color
91
- // Steps 7-10: darker variations
92
- for (let i = 1; i <= 10; i++) {
93
- let color: string;
94
- if (i < 6) {
95
- // Lighter: mix with white
96
- const mixRatio = (6 - i) / 5;
97
- color = lighten(baseColor, mixRatio * 0.8);
98
- } else if (i === 6) {
99
- // Base color
100
- color = baseColor;
101
- } else {
102
- // Darker: mix with black
103
- const mixRatio = (i - 6) / 4;
104
- color = darken(baseColor, mixRatio * 0.6);
105
- }
106
- vars[`${prefix}-${colorName}-${i}`] = color;
83
+ function generateColorScale(
84
+ baseColor: string,
85
+ prefix: string,
86
+ colorName: string
87
+ ): Record<string, string> {
88
+ const vars: Record<string, string> = {};
89
+ const rgb = hexToRgb(baseColor);
90
+ if (!rgb) return vars;
91
+
92
+ // Generate 10-step scale
93
+ // Steps 1-5: lighter variations
94
+ // Step 6: base color
95
+ // Steps 7-10: darker variations
96
+ for (let i = 1; i <= 10; i++) {
97
+ let color: string;
98
+ if (i < 6) {
99
+ // Lighter: mix with white
100
+ const mixRatio = (6 - i) / 5;
101
+ color = lighten(baseColor, mixRatio * 0.8);
102
+ } else if (i === 6) {
103
+ // Base color
104
+ color = baseColor;
105
+ } else {
106
+ // Darker: mix with black
107
+ const mixRatio = (i - 6) / 4;
108
+ color = darken(baseColor, mixRatio * 0.6);
107
109
  }
110
+ vars[`${prefix}-${colorName}-${i}`] = color;
111
+ }
108
112
 
109
- return vars;
113
+ return vars;
110
114
  }
111
115
 
112
116
  /**
113
117
  * Generate CSS variables from theme palette
114
- *
118
+ *
115
119
  * Matches SCSS token naming pattern:
116
120
  * - --atomix-primary (main color)
117
121
  * - --atomix-primary-1 through --atomix-primary-10 (color scale)
@@ -119,224 +123,247 @@ function generateColorScale(baseColor: string, prefix: string, colorName: string
119
123
  * - --atomix-primary-light (alias for primary-3)
120
124
  * - --atomix-primary-dark (alias for primary-9)
121
125
  */
122
- function generatePaletteVariables(palette: Theme['palette'], prefix: string): Record<string, string> {
123
- const vars: Record<string, string> = {};
124
-
125
- // Primary, secondary, error, warning, info, success, light, dark
126
- const colorKeys = ['primary', 'secondary', 'error', 'warning', 'info', 'success', 'light', 'dark'] as const;
127
- colorKeys.forEach((key) => {
128
- const color = palette[key];
129
- if (color && typeof color === 'object') {
130
- // Main color (flat structure, matches SCSS: --atomix-primary)
131
- vars[`${prefix}-${key}`] = color.main;
132
-
133
- // Generate RGB for transparency support (matches SCSS: --atomix-primary-rgb)
134
- const rgb = hexToRgb(color.main);
135
- if (rgb) {
136
- vars[`${prefix}-${key}-rgb`] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
137
- }
138
-
139
- // Generate full color scale (1-10) - matches SCSS: --atomix-primary-1 through --atomix-primary-10
140
- // Only for primary, secondary, error, warning, info, success (not for light/dark)
141
- if (key !== 'light' && key !== 'dark') {
142
- const colorScale = generateColorScale(color.main, prefix, key);
143
- Object.assign(vars, colorScale);
144
- } else {
145
- // For light/dark, use the provided values directly
146
- vars[`${prefix}-${key}-main`] = color.main;
147
- if (color.light) vars[`${prefix}-${key}-light`] = color.light;
148
- if (color.dark) vars[`${prefix}-${key}-dark`] = color.dark;
149
- }
150
-
151
- // Map dark variant to hover (matches SCSS: --atomix-primary-hover)
152
- if (color.dark) {
153
- vars[`${prefix}-${key}-hover`] = color.dark;
154
- }
155
-
156
- // Generate semantic color variants (matches SCSS patterns)
157
- // Text emphasis: emphasized version of the color for text (--atomix-primary-text-emphasis)
158
- vars[`${prefix}-${key}-text-emphasis`] = emphasize(color.main, 0.15);
159
-
160
- // Background subtle: very light version for backgrounds (--atomix-primary-bg-subtle)
161
- vars[`${prefix}-${key}-bg-subtle`] = alpha(color.main, 0.1);
162
-
163
- // Border subtle: light version for borders (--atomix-primary-border-subtle)
164
- vars[`${prefix}-${key}-border-subtle`] = alpha(color.main, 0.2);
165
- }
166
- });
167
-
168
- // Generate gray scale from text colors (matches SCSS: --atomix-gray-1 through --atomix-gray-10)
169
- // Use text.primary as base for gray scale
170
- if (palette.text?.primary) {
171
- const grayScale = generateColorScale(palette.text.primary, prefix, 'gray');
172
- Object.assign(vars, grayScale);
173
- }
174
-
175
- // Generate red, green, blue, yellow scales (matches SCSS: --atomix-red-1 through --atomix-red-10, etc.)
176
- // These are typically used for semantic colors but can be extended
177
- if (palette.error && typeof palette.error === 'object' && palette.error.main) {
178
- const redScale = generateColorScale(palette.error.main, prefix, 'red');
179
- Object.assign(vars, redScale);
180
- }
181
- if (palette.success && typeof palette.success === 'object' && palette.success.main) {
182
- const greenScale = generateColorScale(palette.success.main, prefix, 'green');
183
- Object.assign(vars, greenScale);
184
- }
185
- if (palette.info && typeof palette.info === 'object' && palette.info.main) {
186
- const blueScale = generateColorScale(palette.info.main, prefix, 'blue');
187
- Object.assign(vars, blueScale);
126
+ function generatePaletteVariables(
127
+ palette: Theme['palette'],
128
+ prefix: string
129
+ ): Record<string, string> {
130
+ const vars: Record<string, string> = {};
131
+
132
+ // Primary, secondary, error, warning, info, success, light, dark
133
+ const colorKeys = [
134
+ 'primary',
135
+ 'secondary',
136
+ 'error',
137
+ 'warning',
138
+ 'info',
139
+ 'success',
140
+ 'light',
141
+ 'dark',
142
+ ] as const;
143
+ colorKeys.forEach(key => {
144
+ const color = palette[key];
145
+ if (color && typeof color === 'object') {
146
+ // Main color (flat structure, matches SCSS: --atomix-primary)
147
+ vars[`${prefix}-${key}`] = color.main;
148
+
149
+ // Generate RGB for transparency support (matches SCSS: --atomix-primary-rgb)
150
+ const rgb = hexToRgb(color.main);
151
+ if (rgb) {
152
+ vars[`${prefix}-${key}-rgb`] = `${rgb.r}, ${rgb.g}, ${rgb.b}`;
153
+ }
154
+
155
+ // Generate full color scale (1-10) - matches SCSS: --atomix-primary-1 through --atomix-primary-10
156
+ // Only for primary, secondary, error, warning, info, success (not for light/dark)
157
+ if (key !== 'light' && key !== 'dark') {
158
+ const colorScale = generateColorScale(color.main, prefix, key);
159
+ Object.assign(vars, colorScale);
160
+ } else {
161
+ // For light/dark, use the provided values directly
162
+ vars[`${prefix}-${key}-main`] = color.main;
163
+ if (color.light) vars[`${prefix}-${key}-light`] = color.light;
164
+ if (color.dark) vars[`${prefix}-${key}-dark`] = color.dark;
165
+ }
166
+
167
+ // Map dark variant to hover (matches SCSS: --atomix-primary-hover)
168
+ if (color.dark) {
169
+ vars[`${prefix}-${key}-hover`] = color.dark;
170
+ }
171
+
172
+ // Generate semantic color variants (matches SCSS patterns)
173
+ // Text emphasis: emphasized version of the color for text (--atomix-primary-text-emphasis)
174
+ vars[`${prefix}-${key}-text-emphasis`] = emphasize(color.main, 0.15);
175
+
176
+ // Background subtle: very light version for backgrounds (--atomix-primary-bg-subtle)
177
+ vars[`${prefix}-${key}-bg-subtle`] = alpha(color.main, 0.1);
178
+
179
+ // Border subtle: light version for borders (--atomix-primary-border-subtle)
180
+ vars[`${prefix}-${key}-border-subtle`] = alpha(color.main, 0.2);
188
181
  }
189
- if (palette.warning && typeof palette.warning === 'object' && palette.warning.main) {
190
- const yellowScale = generateColorScale(palette.warning.main, prefix, 'yellow');
191
- Object.assign(vars, yellowScale);
182
+ });
183
+
184
+ // Generate gray scale from text colors (matches SCSS: --atomix-gray-1 through --atomix-gray-10)
185
+ // Use text.primary as base for gray scale
186
+ if (palette.text?.primary) {
187
+ const grayScale = generateColorScale(palette.text.primary, prefix, 'gray');
188
+ Object.assign(vars, grayScale);
189
+ }
190
+
191
+ // Generate red, green, blue, yellow scales (matches SCSS: --atomix-red-1 through --atomix-red-10, etc.)
192
+ // These are typically used for semantic colors but can be extended
193
+ if (palette.error && typeof palette.error === 'object' && palette.error.main) {
194
+ const redScale = generateColorScale(palette.error.main, prefix, 'red');
195
+ Object.assign(vars, redScale);
196
+ }
197
+ if (palette.success && typeof palette.success === 'object' && palette.success.main) {
198
+ const greenScale = generateColorScale(palette.success.main, prefix, 'green');
199
+ Object.assign(vars, greenScale);
200
+ }
201
+ if (palette.info && typeof palette.info === 'object' && palette.info.main) {
202
+ const blueScale = generateColorScale(palette.info.main, prefix, 'blue');
203
+ Object.assign(vars, blueScale);
204
+ }
205
+ if (palette.warning && typeof palette.warning === 'object' && palette.warning.main) {
206
+ const yellowScale = generateColorScale(palette.warning.main, prefix, 'yellow');
207
+ Object.assign(vars, yellowScale);
208
+ }
209
+
210
+ // Background mappings to SCSS body variables (matches SCSS: --atomix-body-bg)
211
+ if (palette.background) {
212
+ vars[`${prefix}-body-bg`] = palette.background.default;
213
+
214
+ // Generate background subtle variants (matches SCSS: --atomix-primary-bg-subtle, etc.)
215
+ if (palette.background.default) {
216
+ vars[`${prefix}-primary-bg-subtle`] = palette.background.default;
192
217
  }
193
-
194
- // Background mappings to SCSS body variables (matches SCSS: --atomix-body-bg)
195
- if (palette.background) {
196
- vars[`${prefix}-body-bg`] = palette.background.default;
197
-
198
- // Generate background subtle variants (matches SCSS: --atomix-primary-bg-subtle, etc.)
199
- if (palette.background.default) {
200
- vars[`${prefix}-primary-bg-subtle`] = palette.background.default;
201
- }
202
- if (palette.background.paper) {
203
- vars[`${prefix}-secondary-bg-subtle`] = palette.background.paper;
204
- vars[`${prefix}-tertiary-bg-subtle`] = palette.background.paper;
205
- }
206
- if (palette.background.subtle) {
207
- vars[`${prefix}-invert-bg-subtle`] = palette.background.subtle;
208
- }
209
-
210
- // Brand bg subtle (uses primary color with alpha)
211
- if (palette.primary) {
212
- vars[`${prefix}-brand-bg-subtle`] = alpha(palette.primary.main, 0.1);
213
- }
218
+ if (palette.background.paper) {
219
+ vars[`${prefix}-secondary-bg-subtle`] = palette.background.paper;
220
+ vars[`${prefix}-tertiary-bg-subtle`] = palette.background.paper;
214
221
  }
215
-
216
- // Text mappings to SCSS body variables (matches SCSS: --atomix-body-color, --atomix-primary-text-emphasis, etc.)
217
- if (palette.text) {
218
- vars[`${prefix}-body-color`] = palette.text.primary;
219
-
220
- // Generate text emphasis variants (matches SCSS pattern)
221
- if (palette.text.primary) {
222
- vars[`${prefix}-primary-text-emphasis`] = palette.text.primary;
223
- }
224
- if (palette.text.secondary) {
225
- vars[`${prefix}-secondary-text-emphasis`] = palette.text.secondary;
226
- vars[`${prefix}-tertiary-text-emphasis`] = palette.text.secondary;
227
- }
228
- if (palette.text.disabled) {
229
- vars[`${prefix}-disabled-text-emphasis`] = palette.text.disabled;
230
- }
231
-
232
- // Invert text emphasis (opposite of primary)
233
- if (palette.text.primary) {
234
- // Invert would be the opposite - for light themes, use dark; for dark themes, use light
235
- // This is a simplified approach - actual inversion depends on theme mode
236
- vars[`${prefix}-invert-text-emphasis`] = palette.text.primary;
237
- }
222
+ if (palette.background.subtle) {
223
+ vars[`${prefix}-invert-bg-subtle`] = palette.background.subtle;
238
224
  }
239
225
 
240
- // Brand text emphasis (uses primary color) - matches SCSS: --atomix-brand-text-emphasis
226
+ // Brand bg subtle (uses primary color with alpha)
241
227
  if (palette.primary) {
242
- vars[`${prefix}-brand-text-emphasis`] = palette.primary.main;
243
- // Brand border subtle - matches SCSS: --atomix-brand-border-subtle
244
- vars[`${prefix}-brand-border-subtle`] = alpha(palette.primary.main, 0.2);
245
- }
246
-
247
- // Light and dark border subtle (if light/dark colors exist) - matches SCSS pattern
248
- if (palette.light && typeof palette.light === 'object') {
249
- vars[`${prefix}-light-border-subtle`] = alpha(palette.light.main, 0.2);
250
- }
251
- if (palette.dark && typeof palette.dark === 'object') {
252
- vars[`${prefix}-dark-border-subtle`] = alpha(palette.dark.main, 0.2);
228
+ vars[`${prefix}-brand-bg-subtle`] = alpha(palette.primary.main, 0.1);
253
229
  }
230
+ }
254
231
 
255
- // Heading color (defaults to text primary) - matches SCSS: --atomix-heading-color
256
- if (palette.text) {
257
- vars[`${prefix}-heading-color`] = palette.text.primary;
258
- }
232
+ // Text mappings to SCSS body variables (matches SCSS: --atomix-body-color, --atomix-primary-text-emphasis, etc.)
233
+ if (palette.text) {
234
+ vars[`${prefix}-body-color`] = palette.text.primary;
259
235
 
260
- // Link colors (defaults to primary color)
261
- if (palette.primary) {
262
- vars[`${prefix}-link-color`] = palette.primary.main;
263
- const linkRgb = hexToRgb(palette.primary.main);
264
- if (linkRgb) {
265
- vars[`${prefix}-link-color-rgb`] = `${linkRgb.r}, ${linkRgb.g}, ${linkRgb.b}`;
266
- }
267
- // Link hover color (slightly darker)
268
- vars[`${prefix}-link-hover-color`] = palette.primary.dark || darken(palette.primary.main, 0.1);
269
- const linkHoverRgb = hexToRgb(palette.primary.dark || darken(palette.primary.main, 0.1));
270
- if (linkHoverRgb) {
271
- vars[`${prefix}-link-hover-color-rgb`] = `${linkHoverRgb.r}, ${linkHoverRgb.g}, ${linkHoverRgb.b}`;
272
- }
273
- // Link decoration (default: none, matching tokens list)
274
- vars[`${prefix}-link-decoration`] = 'none';
236
+ // Generate text emphasis variants (matches SCSS pattern)
237
+ if (palette.text.primary) {
238
+ vars[`${prefix}-primary-text-emphasis`] = palette.text.primary;
275
239
  }
276
-
277
- // Border color (defaults to subtle gray)
278
- if (palette.text) {
279
- vars[`${prefix}-border-color`] = alpha(palette.text.primary, 0.1);
280
- vars[`${prefix}-border-color-translucent`] = alpha(palette.text.primary, 0.15);
240
+ if (palette.text.secondary) {
241
+ vars[`${prefix}-secondary-text-emphasis`] = palette.text.secondary;
242
+ vars[`${prefix}-tertiary-text-emphasis`] = palette.text.secondary;
281
243
  }
282
-
283
- // Focus border color (defaults to primary)
284
- if (palette.primary) {
285
- vars[`${prefix}-focus-border-color`] = palette.primary.main;
244
+ if (palette.text.disabled) {
245
+ vars[`${prefix}-disabled-text-emphasis`] = palette.text.disabled;
286
246
  }
287
247
 
288
- // Form validation colors
289
- if (palette.success) {
290
- vars[`${prefix}-form-valid-color`] = palette.success.main;
291
- vars[`${prefix}-form-valid-border-color`] = alpha(palette.success.main, 0.3);
248
+ // Invert text emphasis (opposite of primary)
249
+ if (palette.text.primary) {
250
+ // Invert would be the opposite - for light themes, use dark; for dark themes, use light
251
+ // This is a simplified approach - actual inversion depends on theme mode
252
+ vars[`${prefix}-invert-text-emphasis`] = palette.text.primary;
292
253
  }
293
- if (palette.error) {
294
- vars[`${prefix}-form-invalid-color`] = palette.error.main;
295
- vars[`${prefix}-form-invalid-border-color`] = alpha(palette.error.main, 0.3);
254
+ }
255
+
256
+ // Brand text emphasis (uses primary color) - matches SCSS: --atomix-brand-text-emphasis
257
+ if (palette.primary) {
258
+ vars[`${prefix}-brand-text-emphasis`] = palette.primary.main;
259
+ // Brand border subtle - matches SCSS: --atomix-brand-border-subtle
260
+ vars[`${prefix}-brand-border-subtle`] = alpha(palette.primary.main, 0.2);
261
+ }
262
+
263
+ // Light and dark border subtle (if light/dark colors exist) - matches SCSS pattern
264
+ if (palette.light && typeof palette.light === 'object') {
265
+ vars[`${prefix}-light-border-subtle`] = alpha(palette.light.main, 0.2);
266
+ }
267
+ if (palette.dark && typeof palette.dark === 'object') {
268
+ vars[`${prefix}-dark-border-subtle`] = alpha(palette.dark.main, 0.2);
269
+ }
270
+
271
+ // Heading color (defaults to text primary) - matches SCSS: --atomix-heading-color
272
+ if (palette.text) {
273
+ vars[`${prefix}-heading-color`] = palette.text.primary;
274
+ }
275
+
276
+ // Link colors (defaults to primary color)
277
+ if (palette.primary) {
278
+ vars[`${prefix}-link-color`] = palette.primary.main;
279
+ const linkRgb = hexToRgb(palette.primary.main);
280
+ if (linkRgb) {
281
+ vars[`${prefix}-link-color-rgb`] = `${linkRgb.r}, ${linkRgb.g}, ${linkRgb.b}`;
296
282
  }
297
-
298
- // Code/highlight colors
299
- // Highlight background (defaults to subtle yellow)
300
- if (palette.warning) {
301
- vars[`${prefix}-highlight-bg`] = alpha(palette.warning.main, 0.2);
302
- } else {
303
- vars[`${prefix}-highlight-bg`] = 'rgba(255, 235, 59, 0.2)';
283
+ // Link hover color (slightly darker)
284
+ vars[`${prefix}-link-hover-color`] = palette.primary.dark || darken(palette.primary.main, 0.1);
285
+ const linkHoverRgb = hexToRgb(palette.primary.dark || darken(palette.primary.main, 0.1));
286
+ if (linkHoverRgb) {
287
+ vars[`${prefix}-link-hover-color-rgb`] =
288
+ `${linkHoverRgb.r}, ${linkHoverRgb.g}, ${linkHoverRgb.b}`;
304
289
  }
305
-
306
- // Code color (defaults to text secondary)
307
- if (palette.text) {
308
- vars[`${prefix}-code-color`] = palette.text.secondary;
290
+ // Link decoration (default: none, matching tokens list)
291
+ vars[`${prefix}-link-decoration`] = 'none';
292
+ }
293
+
294
+ // Border color (defaults to subtle gray)
295
+ if (palette.text) {
296
+ vars[`${prefix}-border-color`] = alpha(palette.text.primary, 0.1);
297
+ vars[`${prefix}-border-color-translucent`] = alpha(palette.text.primary, 0.15);
298
+ }
299
+
300
+ // Focus border color (defaults to primary)
301
+ if (palette.primary) {
302
+ vars[`${prefix}-focus-border-color`] = palette.primary.main;
303
+ }
304
+
305
+ // Form validation colors
306
+ if (palette.success) {
307
+ vars[`${prefix}-form-valid-color`] = palette.success.main;
308
+ vars[`${prefix}-form-valid-border-color`] = alpha(palette.success.main, 0.3);
309
+ }
310
+ if (palette.error) {
311
+ vars[`${prefix}-form-invalid-color`] = palette.error.main;
312
+ vars[`${prefix}-form-invalid-border-color`] = alpha(palette.error.main, 0.3);
313
+ }
314
+
315
+ // Code/highlight colors
316
+ // Highlight background (defaults to subtle yellow)
317
+ if (palette.warning) {
318
+ vars[`${prefix}-highlight-bg`] = alpha(palette.warning.main, 0.2);
319
+ } else {
320
+ vars[`${prefix}-highlight-bg`] = 'rgba(255, 235, 59, 0.2)';
321
+ }
322
+
323
+ // Code color (defaults to text secondary)
324
+ if (palette.text) {
325
+ vars[`${prefix}-code-color`] = palette.text.secondary;
326
+ }
327
+
328
+ // Generate gradient tokens for all colors
329
+ // Gradients use the color scale (lighter to darker variations)
330
+ const gradientColors = [
331
+ 'primary',
332
+ 'secondary',
333
+ 'error',
334
+ 'warning',
335
+ 'info',
336
+ 'success',
337
+ 'light',
338
+ 'dark',
339
+ ] as const;
340
+ gradientColors.forEach(key => {
341
+ const color = palette[key];
342
+ if (color && typeof color === 'object') {
343
+ // Generate gradient using color scale steps
344
+ // Use steps 1, 3, 5 from the color scale for a smooth gradient
345
+ const color1 = lighten(color.main, 0.6);
346
+ const color2 = lighten(color.main, 0.3);
347
+ const color3 = color.main;
348
+ vars[`${prefix}-${key}-gradient`] =
349
+ `linear-gradient(135deg, ${color1}, ${color2}, ${color3})`;
309
350
  }
351
+ });
310
352
 
311
- // Generate gradient tokens for all colors
312
- // Gradients use the color scale (lighter to darker variations)
313
- const gradientColors = ['primary', 'secondary', 'error', 'warning', 'info', 'success', 'light', 'dark'] as const;
314
- gradientColors.forEach((key) => {
315
- const color = palette[key];
316
- if (color && typeof color === 'object') {
317
- // Generate gradient using color scale steps
318
- // Use steps 1, 3, 5 from the color scale for a smooth gradient
319
- const color1 = lighten(color.main, 0.6);
320
- const color2 = lighten(color.main, 0.3);
321
- const color3 = color.main;
322
- vars[`${prefix}-${key}-gradient`] = `linear-gradient(135deg, ${color1}, ${color2}, ${color3})`;
323
- }
324
- });
325
-
326
- // Default gradient (uses gray scale)
327
- if (palette.text?.primary) {
328
- const gray1 = lighten(palette.text.primary, 0.8);
329
- const gray2 = lighten(palette.text.primary, 0.6);
330
- const gray3 = lighten(palette.text.primary, 0.4);
331
- vars[`${prefix}-gradient`] = `linear-gradient(135deg, ${gray1}, ${gray2}, ${gray3})`;
332
- }
353
+ // Default gradient (uses gray scale)
354
+ if (palette.text?.primary) {
355
+ const gray1 = lighten(palette.text.primary, 0.8);
356
+ const gray2 = lighten(palette.text.primary, 0.6);
357
+ const gray3 = lighten(palette.text.primary, 0.4);
358
+ vars[`${prefix}-gradient`] = `linear-gradient(135deg, ${gray1}, ${gray2}, ${gray3})`;
359
+ }
333
360
 
334
- return vars;
361
+ return vars;
335
362
  }
336
363
 
337
364
  /**
338
365
  * Generate CSS variables from theme typography
339
- *
366
+ *
340
367
  * Matches SCSS token naming pattern:
341
368
  * - --atomix-body-font-family
342
369
  * - --atomix-body-font-size
@@ -344,493 +371,509 @@ function generatePaletteVariables(palette: Theme['palette'], prefix: string): Re
344
371
  * - --atomix-line-height-base
345
372
  */
346
373
  function generateTypographyVariables(
347
- typography: Theme['typography'],
348
- prefix: string
374
+ typography: Theme['typography'],
375
+ prefix: string
349
376
  ): Record<string, string> {
350
- const vars: Record<string, string> = {};
351
-
352
- // Font family (matches SCSS: --atomix-body-font-family, --atomix-font-sans-serif, --atomix-font-monospace)
353
- vars[`${prefix}-body-font-family`] = typography.fontFamily;
354
- vars[`${prefix}-font-sans-serif`] = typography.fontFamily;
355
- vars[`${prefix}-font-monospace`] = 'SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
356
-
357
- // Root font size (matches SCSS: --atomix-root-font-size)
358
- // Typically 16px, but can be customized
359
- const rootFontSize = typography.fontSize || 16;
360
- vars[`${prefix}-root-font-size`] = `${rootFontSize}px`;
361
-
362
- // Base font size (matches SCSS: --atomix-body-font-size)
363
- const baseFontSize = typography.fontSize;
364
- vars[`${prefix}-body-font-size`] = `${baseFontSize}px`;
365
-
366
- // Base font weight (matches SCSS: --atomix-body-font-weight)
367
- vars[`${prefix}-body-font-weight`] = String(typography.fontWeightRegular);
368
-
369
- // Font weight scale (matches SCSS: --atomix-font-weight-light, --atomix-font-weight-normal, etc.)
370
- vars[`${prefix}-font-weight-light`] = String(typography.fontWeightLight ?? 300);
371
- vars[`${prefix}-font-weight-normal`] = String(typography.fontWeightRegular ?? 400);
372
- vars[`${prefix}-font-weight-medium`] = String(typography.fontWeightMedium ?? 500);
373
- vars[`${prefix}-font-weight-semibold`] = String(typography.fontWeightSemiBold ?? 600);
374
- vars[`${prefix}-font-weight-bold`] = String(typography.fontWeightBold ?? 700);
375
- // Optional font weights (may not be in theme, but exist in design tokens)
376
- if ('fontWeightHeavy' in typography) {
377
- vars[`${prefix}-font-weight-heavy`] = String((typography as any).fontWeightHeavy || 800);
378
- }
379
- if ('fontWeightBlack' in typography) {
380
- vars[`${prefix}-font-weight-black`] = String((typography as any).fontWeightBlack || 900);
381
- }
382
-
383
- // Base line height (matches SCSS: --atomix-body-line-height)
384
- const baseLineHeight = typeof typography.body1?.lineHeight === 'number'
385
- ? typography.body1.lineHeight
386
- : parseFloat(String(typography.body1?.lineHeight || 1.2));
387
- vars[`${prefix}-body-line-height`] = String(baseLineHeight);
388
-
389
- // Line height scale (matches SCSS: --atomix-line-height-base, --atomix-line-height-sm, --atomix-line-height-lg)
390
- vars[`${prefix}-line-height-base`] = String(baseLineHeight);
391
- vars[`${prefix}-line-height-sm`] = String(1.43);
392
- vars[`${prefix}-line-height-lg`] = String(1.56);
393
-
394
- // Extended font size scale (matches SCSS: --atomix-font-size-xs, --atomix-font-size-sm, etc.)
395
- const fontSizeXs = baseFontSize * 0.75; // 12px if base is 16px
396
- const fontSizeSm = baseFontSize * 0.875; // 14px if base is 16px
397
- const fontSizeMd = baseFontSize * 1; // 16px if base is 16px (same as base)
398
- const fontSizeLg = baseFontSize * 1.125; // 18px if base is 16px
399
- const fontSizeXl = baseFontSize * 1.5; // 24px if base is 16px
400
- const fontSize2xl = baseFontSize * 2; // 32px if base is 16px
401
-
402
- vars[`${prefix}-font-size-xs`] = `${fontSizeXs}px`;
403
- vars[`${prefix}-font-size-sm`] = `${fontSizeSm}px`;
404
- vars[`${prefix}-font-size-md`] = `${fontSizeMd}px`;
405
- vars[`${prefix}-font-size-lg`] = `${fontSizeLg}px`;
406
- vars[`${prefix}-font-size-xl`] = `${fontSizeXl}px`;
407
- vars[`${prefix}-font-size-2xl`] = `${fontSize2xl}px`;
408
-
409
- // Display font size (matches SCSS: --atomix-display-1)
410
- if ('display1' in typography) {
411
- const display1 = (typography as any).display1;
412
- vars[`${prefix}-display-1`] = typeof display1 === 'string' ? display1 : `${display1}px`;
377
+ const vars: Record<string, string> = {};
378
+
379
+ // Font family (matches SCSS: --atomix-body-font-family, --atomix-font-sans-serif, --atomix-font-monospace)
380
+ vars[`${prefix}-body-font-family`] = typography.fontFamily;
381
+ vars[`${prefix}-font-sans-serif`] = typography.fontFamily;
382
+ vars[`${prefix}-font-monospace`] =
383
+ 'SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
384
+
385
+ // Root font size (matches SCSS: --atomix-root-font-size)
386
+ // Typically 16px, but can be customized
387
+ const rootFontSize = typography.fontSize || 16;
388
+ vars[`${prefix}-root-font-size`] = `${rootFontSize}px`;
389
+
390
+ // Base font size (matches SCSS: --atomix-body-font-size)
391
+ const baseFontSize = typography.fontSize;
392
+ vars[`${prefix}-body-font-size`] = `${baseFontSize}px`;
393
+
394
+ // Base font weight (matches SCSS: --atomix-body-font-weight)
395
+ vars[`${prefix}-body-font-weight`] = String(typography.fontWeightRegular);
396
+
397
+ // Font weight scale (matches SCSS: --atomix-font-weight-light, --atomix-font-weight-normal, etc.)
398
+ vars[`${prefix}-font-weight-light`] = String(typography.fontWeightLight ?? 300);
399
+ vars[`${prefix}-font-weight-normal`] = String(typography.fontWeightRegular ?? 400);
400
+ vars[`${prefix}-font-weight-medium`] = String(typography.fontWeightMedium ?? 500);
401
+ vars[`${prefix}-font-weight-semibold`] = String(typography.fontWeightSemiBold ?? 600);
402
+ vars[`${prefix}-font-weight-bold`] = String(typography.fontWeightBold ?? 700);
403
+ // Optional font weights (may not be in theme, but exist in design tokens)
404
+ if ('fontWeightHeavy' in typography) {
405
+ vars[`${prefix}-font-weight-heavy`] = String((typography as any).fontWeightHeavy || 800);
406
+ }
407
+ if ('fontWeightBlack' in typography) {
408
+ vars[`${prefix}-font-weight-black`] = String((typography as any).fontWeightBlack || 900);
409
+ }
410
+
411
+ // Base line height (matches SCSS: --atomix-body-line-height)
412
+ const baseLineHeight =
413
+ typeof typography.body1?.lineHeight === 'number'
414
+ ? typography.body1.lineHeight
415
+ : parseFloat(String(typography.body1?.lineHeight || 1.2));
416
+ vars[`${prefix}-body-line-height`] = String(baseLineHeight);
417
+
418
+ // Line height scale (matches SCSS: --atomix-line-height-base, --atomix-line-height-sm, --atomix-line-height-lg)
419
+ vars[`${prefix}-line-height-base`] = String(baseLineHeight);
420
+ vars[`${prefix}-line-height-sm`] = String(1.43);
421
+ vars[`${prefix}-line-height-lg`] = String(1.56);
422
+
423
+ // Extended font size scale (matches SCSS: --atomix-font-size-xs, --atomix-font-size-sm, etc.)
424
+ const fontSizeXs = baseFontSize * 0.75; // 12px if base is 16px
425
+ const fontSizeSm = baseFontSize * 0.875; // 14px if base is 16px
426
+ const fontSizeMd = baseFontSize * 1; // 16px if base is 16px (same as base)
427
+ const fontSizeLg = baseFontSize * 1.125; // 18px if base is 16px
428
+ const fontSizeXl = baseFontSize * 1.5; // 24px if base is 16px
429
+ const fontSize2xl = baseFontSize * 2; // 32px if base is 16px
430
+
431
+ vars[`${prefix}-font-size-xs`] = `${fontSizeXs}px`;
432
+ vars[`${prefix}-font-size-sm`] = `${fontSizeSm}px`;
433
+ vars[`${prefix}-font-size-md`] = `${fontSizeMd}px`;
434
+ vars[`${prefix}-font-size-lg`] = `${fontSizeLg}px`;
435
+ vars[`${prefix}-font-size-xl`] = `${fontSizeXl}px`;
436
+ vars[`${prefix}-font-size-2xl`] = `${fontSize2xl}px`;
437
+
438
+ // Display font size (matches SCSS: --atomix-display-1)
439
+ if ('display1' in typography) {
440
+ const display1 = (typography as any).display1;
441
+ vars[`${prefix}-display-1`] = typeof display1 === 'string' ? display1 : `${display1}px`;
442
+ }
443
+
444
+ // Letter spacing for headings (matches SCSS: --atomix-letter-spacing-h1, etc.)
445
+ const headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
446
+ headings.forEach(heading => {
447
+ const headingConfig = typography[heading];
448
+ if (headingConfig?.letterSpacing) {
449
+ vars[`${prefix}-letter-spacing-${heading}`] = String(headingConfig.letterSpacing);
413
450
  }
451
+ });
414
452
 
415
- // Letter spacing for headings (matches SCSS: --atomix-letter-spacing-h1, etc.)
416
- const headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
417
- headings.forEach((heading) => {
418
- const headingConfig = typography[heading];
419
- if (headingConfig?.letterSpacing) {
420
- vars[`${prefix}-letter-spacing-${heading}`] = String(headingConfig.letterSpacing);
421
- }
422
- });
423
-
424
- return vars;
453
+ return vars;
425
454
  }
426
455
 
427
456
  /**
428
457
  * Generate CSS variables from theme shadows
429
- *
458
+ *
430
459
  * Matches SCSS token naming pattern:
431
460
  * - --atomix-box-shadow (base, mapped from md)
432
461
  * - --atomix-box-shadow-xs, --atomix-box-shadow-sm, --atomix-box-shadow-lg, --atomix-box-shadow-xl
433
462
  * - --atomix-box-shadow-inset
434
463
  */
435
- function generateShadowVariables(shadows: Theme['shadows'], prefix: string): Record<string, string> {
436
- const vars: Record<string, string> = {};
437
-
438
- // Map JS shadow keys to SCSS variables (matches SCSS pattern exactly)
439
- if (shadows.md) vars[`${prefix}-box-shadow`] = shadows.md; // Map md to base
440
- if (shadows.xs) vars[`${prefix}-box-shadow-xs`] = shadows.xs;
441
- if (shadows.sm) vars[`${prefix}-box-shadow-sm`] = shadows.sm;
442
- if (shadows.lg) vars[`${prefix}-box-shadow-lg`] = shadows.lg;
443
- if (shadows.xl) vars[`${prefix}-box-shadow-xl`] = shadows.xl;
444
-
445
- // Inset shadow (matches SCSS: --atomix-box-shadow-inset)
446
- if (shadows.inset) {
447
- vars[`${prefix}-box-shadow-inset`] = shadows.inset;
448
- } else if (shadows.sm) {
449
- // Generate inset shadow from sm shadow
450
- vars[`${prefix}-box-shadow-inset`] = shadows.sm.replace(/^0\s/, 'inset ');
451
- }
452
-
453
- return vars;
464
+ function generateShadowVariables(
465
+ shadows: Theme['shadows'],
466
+ prefix: string
467
+ ): Record<string, string> {
468
+ const vars: Record<string, string> = {};
469
+
470
+ // Map JS shadow keys to SCSS variables (matches SCSS pattern exactly)
471
+ if (shadows.md) vars[`${prefix}-box-shadow`] = shadows.md; // Map md to base
472
+ if (shadows.xs) vars[`${prefix}-box-shadow-xs`] = shadows.xs;
473
+ if (shadows.sm) vars[`${prefix}-box-shadow-sm`] = shadows.sm;
474
+ if (shadows.lg) vars[`${prefix}-box-shadow-lg`] = shadows.lg;
475
+ if (shadows.xl) vars[`${prefix}-box-shadow-xl`] = shadows.xl;
476
+
477
+ // Inset shadow (matches SCSS: --atomix-box-shadow-inset)
478
+ if (shadows.inset) {
479
+ vars[`${prefix}-box-shadow-inset`] = shadows.inset;
480
+ } else if (shadows.sm) {
481
+ // Generate inset shadow from sm shadow
482
+ vars[`${prefix}-box-shadow-inset`] = shadows.sm.replace(/^0\s/, 'inset ');
483
+ }
484
+
485
+ return vars;
454
486
  }
455
487
 
456
488
  /**
457
489
  * Generate CSS variables from theme transitions
458
490
  */
459
491
  function generateTransitionVariables(
460
- transitions: Theme['transitions'],
461
- prefix: string
492
+ transitions: Theme['transitions'],
493
+ prefix: string
462
494
  ): Record<string, string> {
463
- const vars: Record<string, string> = {};
464
-
465
- // Map JS transition durations to SCSS equivalents (in seconds to match design tokens)
466
- const durationFast = transitions.duration.shortest || 150;
467
- const durationBase = transitions.duration.standard || 300;
468
- const durationSlow = transitions.duration.complex || 500;
469
- const durationSlower = 700; // Default value for slower duration
470
- const easingBase = transitions.easing.easeInOut || 'cubic-bezier(0.23, 1, 0.32, 1)';
471
-
472
- vars[`${prefix}-transition-duration-fast`] = `${durationFast / 1000}s`;
473
- vars[`${prefix}-transition-duration-base`] = `${durationBase / 1000}s`;
474
- vars[`${prefix}-transition-duration-slow`] = `${durationSlow / 1000}s`;
475
- vars[`${prefix}-transition-duration-slower`] = `${durationSlower / 1000}s`;
476
-
477
- // Map easing functions
478
- vars[`${prefix}-easing-base`] = easingBase;
479
- vars[`${prefix}-easing-ease-in-out`] = transitions.easing.easeInOut || 'cubic-bezier(0.4, 0, 0.2, 1)';
480
- vars[`${prefix}-easing-ease-out`] = transitions.easing.easeOut || 'cubic-bezier(0, 0, 0.2, 1)';
481
- vars[`${prefix}-easing-ease-in`] = transitions.easing.easeIn || 'cubic-bezier(0.4, 0, 1, 1)';
482
- vars[`${prefix}-easing-ease-linear`] = 'linear';
483
-
484
- // Generate full transition strings
485
- vars[`${prefix}-transition-fast`] = `all ${durationFast / 1000}s ${easingBase}`;
486
- vars[`${prefix}-transition-base`] = `all ${durationBase / 1000}s ${easingBase}`;
487
- vars[`${prefix}-transition-slow`] = `all ${durationSlow / 1000}s ${easingBase}`;
488
-
489
- return vars;
495
+ const vars: Record<string, string> = {};
496
+
497
+ // Map JS transition durations to SCSS equivalents (in seconds to match design tokens)
498
+ const durationFast = transitions.duration.shortest || 150;
499
+ const durationBase = transitions.duration.standard || 300;
500
+ const durationSlow = transitions.duration.complex || 500;
501
+ const durationSlower = 700; // Default value for slower duration
502
+ const easingBase = transitions.easing.easeInOut || 'cubic-bezier(0.23, 1, 0.32, 1)';
503
+
504
+ vars[`${prefix}-transition-duration-fast`] = `${durationFast / 1000}s`;
505
+ vars[`${prefix}-transition-duration-base`] = `${durationBase / 1000}s`;
506
+ vars[`${prefix}-transition-duration-slow`] = `${durationSlow / 1000}s`;
507
+ vars[`${prefix}-transition-duration-slower`] = `${durationSlower / 1000}s`;
508
+
509
+ // Map easing functions
510
+ vars[`${prefix}-easing-base`] = easingBase;
511
+ vars[`${prefix}-easing-ease-in-out`] =
512
+ transitions.easing.easeInOut || 'cubic-bezier(0.4, 0, 0.2, 1)';
513
+ vars[`${prefix}-easing-ease-out`] = transitions.easing.easeOut || 'cubic-bezier(0, 0, 0.2, 1)';
514
+ vars[`${prefix}-easing-ease-in`] = transitions.easing.easeIn || 'cubic-bezier(0.4, 0, 1, 1)';
515
+ vars[`${prefix}-easing-ease-linear`] = 'linear';
516
+
517
+ // Generate full transition strings
518
+ vars[`${prefix}-transition-fast`] = `all ${durationFast / 1000}s ${easingBase}`;
519
+ vars[`${prefix}-transition-base`] = `all ${durationBase / 1000}s ${easingBase}`;
520
+ vars[`${prefix}-transition-slow`] = `all ${durationSlow / 1000}s ${easingBase}`;
521
+
522
+ return vars;
490
523
  }
491
524
 
492
525
  /**
493
526
  * Generate CSS variables from theme z-index
494
527
  */
495
528
  function generateZIndexVariables(zIndex: Theme['zIndex'], prefix: string): Record<string, string> {
496
- const vars: Record<string, string> = {};
497
-
498
- // Generate z-index scale (matching tokens list)
499
- vars[`${prefix}-z-n1`] = '-1';
500
- vars[`${prefix}-z-0`] = '0';
501
- vars[`${prefix}-z-1`] = '1';
502
- vars[`${prefix}-z-2`] = '2';
503
- vars[`${prefix}-z-3`] = '3';
504
- vars[`${prefix}-z-4`] = '4';
505
- vars[`${prefix}-z-5`] = '5';
506
-
507
- // Map to SCSS z-layers (semantic z-index values)
508
- if (zIndex.mobileStepper) vars[`${prefix}-z-dropdown`] = String(zIndex.mobileStepper);
509
- if (zIndex.appBar) vars[`${prefix}-z-sticky`] = String(zIndex.appBar);
510
- vars[`${prefix}-z-fixed`] = '1030'; // Default fixed
511
- if (zIndex.modal) vars[`${prefix}-z-modal`] = String(zIndex.modal);
512
- if (zIndex.speedDial) vars[`${prefix}-z-popover`] = String(zIndex.speedDial);
513
- if (zIndex.tooltip) vars[`${prefix}-z-tooltip`] = String(zIndex.tooltip);
514
- if (zIndex.drawer) vars[`${prefix}-z-drawer`] = String(zIndex.drawer);
515
-
516
- // Keep original mappings if needed or remove if strictly aligning
517
- if (zIndex.snackbar) vars[`${prefix}-z-snackbar`] = String(zIndex.snackbar);
518
-
519
- return vars;
529
+ const vars: Record<string, string> = {};
530
+
531
+ // Generate z-index scale (matching tokens list)
532
+ vars[`${prefix}-z-n1`] = '-1';
533
+ vars[`${prefix}-z-0`] = '0';
534
+ vars[`${prefix}-z-1`] = '1';
535
+ vars[`${prefix}-z-2`] = '2';
536
+ vars[`${prefix}-z-3`] = '3';
537
+ vars[`${prefix}-z-4`] = '4';
538
+ vars[`${prefix}-z-5`] = '5';
539
+
540
+ // Map to SCSS z-layers (semantic z-index values)
541
+ if (zIndex.mobileStepper) vars[`${prefix}-z-dropdown`] = String(zIndex.mobileStepper);
542
+ if (zIndex.appBar) vars[`${prefix}-z-sticky`] = String(zIndex.appBar);
543
+ vars[`${prefix}-z-fixed`] = '1030'; // Default fixed
544
+ if (zIndex.modal) vars[`${prefix}-z-modal`] = String(zIndex.modal);
545
+ if (zIndex.speedDial) vars[`${prefix}-z-popover`] = String(zIndex.speedDial);
546
+ if (zIndex.tooltip) vars[`${prefix}-z-tooltip`] = String(zIndex.tooltip);
547
+ if (zIndex.drawer) vars[`${prefix}-z-drawer`] = String(zIndex.drawer);
548
+
549
+ // Keep original mappings if needed or remove if strictly aligning
550
+ if (zIndex.snackbar) vars[`${prefix}-z-snackbar`] = String(zIndex.snackbar);
551
+
552
+ return vars;
520
553
  }
521
554
 
522
555
  /**
523
556
  * Generate CSS variables from theme breakpoints
524
557
  */
525
558
  function generateBreakpointVariables(
526
- breakpoints: Theme['breakpoints'],
527
- prefix: string
559
+ breakpoints: Theme['breakpoints'],
560
+ prefix: string
528
561
  ): Record<string, string> {
529
- const vars: Record<string, string> = {};
562
+ const vars: Record<string, string> = {};
530
563
 
531
- Object.entries(breakpoints.values).forEach(([key, value]) => {
532
- vars[`${prefix}-breakpoint-${key}`] = `${value}${breakpoints.unit}`;
533
- });
564
+ Object.entries(breakpoints.values).forEach(([key, value]) => {
565
+ vars[`${prefix}-breakpoint-${key}`] = `${value}${breakpoints.unit}`;
566
+ });
534
567
 
535
- return vars;
568
+ return vars;
536
569
  }
537
570
 
538
571
  /**
539
572
  * Generate CSS variables from theme spacing
540
573
  */
541
574
  function generateSpacingVariables(
542
- spacing: Theme['spacing'],
543
- prefix: string
575
+ spacing: Theme['spacing'],
576
+ prefix: string
544
577
  ): Record<string, string> {
545
- const vars: Record<string, string> = {};
546
-
547
- // Generate spacing scale based on design system tokens
548
- // Note: Some spacing values like px-6, px-10, etc. are generated from the spacing function
549
- // and should match the actual design system spacing scale
550
- // Values are multipliers for the spacing base unit (default 4px)
551
- const spacingScale: Record<string, number> = {
552
- '0': 0,
553
- '1': 1, // 4px (1 × 4 = 4px)
554
- 'px-6': 1.5, // 6px (1.5 × 4 = 6px)
555
- '2': 2, // 8px (2 × 4 = 8px)
556
- 'px-10': 2.5, // 10px (2.5 × 4 = 10px)
557
- '3': 3, // 12px (3 × 4 = 12px)
558
- 'px-14': 3.5, // 14px (3.5 × 4 = 14px)
559
- '4': 4, // 16px (4 × 4 = 16px)
560
- '5': 5, // 20px (5 × 4 = 20px)
561
- 'px-22': 5.5, // 22px (5.5 × 4 = 22px)
562
- '6': 6, // 24px (6 × 4 = 24px)
563
- '7': 7, // 28px (7 × 4 = 28px)
564
- 'px-30': 7.5, // 30px (7.5 × 4 = 30px)
565
- '8': 8, // 32px (8 × 4 = 32px)
566
- '9': 9, // 36px (9 × 4 = 36px)
567
- '10': 10, // 40px (10 × 4 = 40px)
568
- '11': 11, // 44px (11 × 4 = 44px)
569
- '12': 12, // 48px (12 × 4 = 48px)
570
- '14': 14, // 56px (14 × 4 = 56px)
571
- '16': 16, // 64px (16 × 4 = 64px)
572
- '20': 20, // 80px (20 × 4 = 80px)
573
- '24': 24, // 96px (24 × 4 = 96px)
574
- '28': 28, // 112px (28 × 4 = 112px)
575
- '32': 32, // 128px (32 × 4 = 128px)
576
- '36': 36, // 144px (36 × 4 = 144px)
577
- '40': 40, // 160px (40 × 4 = 160px)
578
- '44': 44, // 176px (44 × 4 = 176px)
579
- '48': 48, // 192px (48 × 4 = 192px)
580
- '52': 52, // 208px (52 × 4 = 208px)
581
- '56': 56, // 224px (56 × 4 = 224px)
582
- '60': 60, // 240px (60 × 4 = 240px)
583
- '64': 64, // 256px (64 × 4 = 256px)
584
- '72': 72, // 288px (72 × 4 = 288px)
585
- '80': 80, // 320px (80 × 4 = 320px)
586
- '90': 90, // 360px (90 × 4 = 360px)
587
- '200': 200, // 800px (200 × 4 = 800px)
588
- };
589
-
590
- // Generate spacing variables
591
- // Use the spacing function to calculate values
592
- Object.entries(spacingScale).forEach(([key, multiplier]) => {
593
- const spacingValue = spacing(multiplier);
594
- // Extract numeric value and convert to rem if needed
595
- const match = spacingValue.match(/([\d.]+)px/);
596
- if (match && match[1]) {
597
- const pxValue = parseFloat(match[1]);
598
- const remValue = pxValue / 16; // Convert px to rem (assuming 16px base)
599
- vars[`${prefix}-spacing-${key}`] = `${remValue}rem`;
600
- } else {
601
- vars[`${prefix}-spacing-${key}`] = spacingValue;
602
- }
603
- });
604
-
605
- return vars;
578
+ const vars: Record<string, string> = {};
579
+
580
+ // Generate spacing scale based on design system tokens
581
+ // Note: Some spacing values like px-6, px-10, etc. are generated from the spacing function
582
+ // and should match the actual design system spacing scale
583
+ // Values are multipliers for the spacing base unit (default 4px)
584
+ const spacingScale: Record<string, number> = {
585
+ '0': 0,
586
+ '1': 1, // 4px (1 × 4 = 4px)
587
+ 'px-6': 1.5, // 6px (1.5 × 4 = 6px)
588
+ '2': 2, // 8px (2 × 4 = 8px)
589
+ 'px-10': 2.5, // 10px (2.5 × 4 = 10px)
590
+ '3': 3, // 12px (3 × 4 = 12px)
591
+ 'px-14': 3.5, // 14px (3.5 × 4 = 14px)
592
+ '4': 4, // 16px (4 × 4 = 16px)
593
+ '5': 5, // 20px (5 × 4 = 20px)
594
+ 'px-22': 5.5, // 22px (5.5 × 4 = 22px)
595
+ '6': 6, // 24px (6 × 4 = 24px)
596
+ '7': 7, // 28px (7 × 4 = 28px)
597
+ 'px-30': 7.5, // 30px (7.5 × 4 = 30px)
598
+ '8': 8, // 32px (8 × 4 = 32px)
599
+ '9': 9, // 36px (9 × 4 = 36px)
600
+ '10': 10, // 40px (10 × 4 = 40px)
601
+ '11': 11, // 44px (11 × 4 = 44px)
602
+ '12': 12, // 48px (12 × 4 = 48px)
603
+ '14': 14, // 56px (14 × 4 = 56px)
604
+ '16': 16, // 64px (16 × 4 = 64px)
605
+ '20': 20, // 80px (20 × 4 = 80px)
606
+ '24': 24, // 96px (24 × 4 = 96px)
607
+ '28': 28, // 112px (28 × 4 = 112px)
608
+ '32': 32, // 128px (32 × 4 = 128px)
609
+ '36': 36, // 144px (36 × 4 = 144px)
610
+ '40': 40, // 160px (40 × 4 = 160px)
611
+ '44': 44, // 176px (44 × 4 = 176px)
612
+ '48': 48, // 192px (48 × 4 = 192px)
613
+ '52': 52, // 208px (52 × 4 = 208px)
614
+ '56': 56, // 224px (56 × 4 = 224px)
615
+ '60': 60, // 240px (60 × 4 = 240px)
616
+ '64': 64, // 256px (64 × 4 = 256px)
617
+ '72': 72, // 288px (72 × 4 = 288px)
618
+ '80': 80, // 320px (80 × 4 = 320px)
619
+ '90': 90, // 360px (90 × 4 = 360px)
620
+ '200': 200, // 800px (200 × 4 = 800px)
621
+ };
622
+
623
+ // Generate spacing variables
624
+ // Use the spacing function to calculate values
625
+ Object.entries(spacingScale).forEach(([key, multiplier]) => {
626
+ const spacingValue = spacing(multiplier);
627
+ // Extract numeric value and convert to rem if needed
628
+ const match = spacingValue.match(/([\d.]+)px/);
629
+ if (match && match[1]) {
630
+ const pxValue = parseFloat(match[1]);
631
+ const remValue = pxValue / 16; // Convert px to rem (assuming 16px base)
632
+ vars[`${prefix}-spacing-${key}`] = `${remValue}rem`;
633
+ } else {
634
+ vars[`${prefix}-spacing-${key}`] = spacingValue;
635
+ }
636
+ });
637
+
638
+ return vars;
606
639
  }
607
640
 
608
641
  /**
609
642
  * Generate border-related CSS variables
610
643
  */
611
644
  function generateBorderVariables(
612
- palette: Theme['palette'],
613
- prefix: string
645
+ palette: Theme['palette'],
646
+ prefix: string
614
647
  ): Record<string, string> {
615
- const vars: Record<string, string> = {};
648
+ const vars: Record<string, string> = {};
616
649
 
617
- // Border width
618
- vars[`${prefix}-border-width`] = '1px';
650
+ // Border width
651
+ vars[`${prefix}-border-width`] = '1px';
619
652
 
620
- // Border style
621
- vars[`${prefix}-border-style`] = 'solid';
653
+ // Border style
654
+ vars[`${prefix}-border-style`] = 'solid';
622
655
 
623
- // Border color (already generated in palette, but ensure it exists)
624
- if (!vars[`${prefix}-border-color`] && palette.text) {
625
- vars[`${prefix}-border-color`] = alpha(palette.text.primary, 0.1);
626
- }
656
+ // Border color (already generated in palette, but ensure it exists)
657
+ if (!vars[`${prefix}-border-color`] && palette.text) {
658
+ vars[`${prefix}-border-color`] = alpha(palette.text.primary, 0.1);
659
+ }
627
660
 
628
- return vars;
661
+ return vars;
629
662
  }
630
663
 
631
664
  /**
632
665
  * Generate border radius CSS variables
633
666
  */
634
667
  function generateBorderRadiusVariables(
635
- borderRadius: Theme['borderRadius'],
636
- prefix: string
668
+ borderRadius: Theme['borderRadius'],
669
+ prefix: string
637
670
  ): Record<string, string> {
638
- const vars: Record<string, string> = {};
671
+ const vars: Record<string, string> = {};
639
672
 
640
- // Convert values to string with proper units
641
- const formatValue = (value: string | number | undefined, defaultValue: string): string => {
642
- if (value === undefined) return defaultValue;
643
- if (typeof value === 'number') return `${value}px`;
644
- return String(value);
645
- };
673
+ // Convert values to string with proper units
674
+ const formatValue = (value: string | number | undefined, defaultValue: string): string => {
675
+ if (value === undefined) return defaultValue;
676
+ if (typeof value === 'number') return `${value}px`;
677
+ return String(value);
678
+ };
646
679
 
647
- // Base border radius (maps to spacing-2 = 8px)
648
- vars[`${prefix}-border-radius`] = formatValue(borderRadius.base, '0.5rem');
680
+ // Base border radius (maps to spacing-2 = 8px)
681
+ vars[`${prefix}-border-radius`] = formatValue(borderRadius.base, '0.5rem');
649
682
 
650
- // Small border radius (maps to spacing-1 = 4px)
651
- vars[`${prefix}-border-radius-sm`] = formatValue(borderRadius.sm, '0.25rem');
683
+ // Small border radius (maps to spacing-1 = 4px)
684
+ vars[`${prefix}-border-radius-sm`] = formatValue(borderRadius.sm, '0.25rem');
652
685
 
653
- // Large border radius (maps to spacing-2.5 = 10px)
654
- vars[`${prefix}-border-radius-lg`] = formatValue(borderRadius.lg, '0.625rem');
686
+ // Large border radius (maps to spacing-2.5 = 10px)
687
+ vars[`${prefix}-border-radius-lg`] = formatValue(borderRadius.lg, '0.625rem');
655
688
 
656
- // Extra large border radius (maps to spacing-3 = 12px)
657
- vars[`${prefix}-border-radius-xl`] = formatValue(borderRadius.xl, '0.75rem');
689
+ // Extra large border radius (maps to spacing-3 = 12px)
690
+ vars[`${prefix}-border-radius-xl`] = formatValue(borderRadius.xl, '0.75rem');
658
691
 
659
- // 2X large border radius (maps to spacing-4 = 16px)
660
- vars[`${prefix}-border-radius-xxl`] = formatValue(borderRadius.xxl, '1rem');
692
+ // 2X large border radius (maps to spacing-4 = 16px)
693
+ vars[`${prefix}-border-radius-xxl`] = formatValue(borderRadius.xxl, '1rem');
661
694
 
662
- // 3X large border radius (maps to spacing-6 = 24px)
663
- vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius['3xl'], '1.5rem');
695
+ // 3X large border radius (maps to spacing-6 = 24px)
696
+ vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius['3xl'], '1.5rem');
664
697
 
665
- // 4X large border radius (maps to spacing-8 = 32px)
666
- vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius['4xl'], '2rem');
698
+ // 4X large border radius (maps to spacing-8 = 32px)
699
+ vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius['4xl'], '2rem');
667
700
 
668
- // Pill shape (fully rounded, maps to spacing-200 = 800px)
669
- vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, '50rem');
701
+ // Pill shape (fully rounded, maps to spacing-200 = 800px)
702
+ vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, '50rem');
670
703
 
671
- return vars;
704
+ return vars;
672
705
  }
673
706
 
674
707
  /**
675
708
  * Generate focus ring CSS variables
676
709
  */
677
710
  function generateFocusRingVariables(
678
- palette: Theme['palette'],
679
- prefix: string
711
+ palette: Theme['palette'],
712
+ prefix: string
680
713
  ): Record<string, string> {
681
- const vars: Record<string, string> = {};
714
+ const vars: Record<string, string> = {};
682
715
 
683
- // Focus ring properties
684
- vars[`${prefix}-focus-ring-width`] = '3px';
685
- vars[`${prefix}-focus-ring-offset`] = '2px';
686
- vars[`${prefix}-focus-ring-opacity`] = '0.25';
716
+ // Focus ring properties
717
+ vars[`${prefix}-focus-ring-width`] = '3px';
718
+ vars[`${prefix}-focus-ring-offset`] = '2px';
719
+ vars[`${prefix}-focus-ring-opacity`] = '0.25';
687
720
 
688
- return vars;
721
+ return vars;
689
722
  }
690
723
 
691
724
  /**
692
725
  * Generate CSS custom properties from a theme object
693
- *
726
+ *
694
727
  * @param theme - Theme object created with createTheme
695
728
  * @param options - Generation options
696
729
  * @returns CSS string with custom properties
697
730
  */
698
731
  export function generateCSSVariables(
699
- theme: Theme,
700
- options: GenerateCSSVariablesOptions = {}
732
+ theme: Theme,
733
+ options: GenerateCSSVariablesOptions = {}
701
734
  ): string {
702
- const {
703
- selector = ':root',
704
- inject = false,
705
- styleId = 'atomix-theme-variables',
706
- prefix = 'atomix',
707
- } = options;
708
-
709
- const variables: Record<string, string> = {};
710
-
711
- // Generate variables from each theme section
712
- Object.assign(variables, generatePaletteVariables(theme.palette, prefix));
713
- Object.assign(variables, generateTypographyVariables(theme.typography, prefix));
714
- Object.assign(variables, generateShadowVariables(theme.shadows, prefix));
715
- Object.assign(variables, generateTransitionVariables(theme.transitions, prefix));
716
- Object.assign(variables, generateZIndexVariables(theme.zIndex, prefix));
717
- Object.assign(variables, generateBreakpointVariables(theme.breakpoints, prefix));
718
- Object.assign(variables, generateSpacingVariables(theme.spacing, prefix));
719
- Object.assign(variables, generateBorderVariables(theme.palette, prefix));
720
- Object.assign(variables, generateBorderRadiusVariables(theme.borderRadius, prefix));
721
- Object.assign(variables, generateFocusRingVariables(theme.palette, prefix));
722
-
723
- // Add custom properties if present
724
- if (theme.custom && Object.keys(theme.custom).length > 0) {
725
- const customVars = flattenObject(theme.custom, `${prefix}-custom`);
726
- Object.assign(variables, customVars);
727
- }
728
-
729
- // Convert to CSS string
730
- const cssVariables = Object.entries(variables)
731
- .map(([key, value]) => ` --${key}: ${value};`)
732
- .join('\n');
733
-
734
- const css = `${selector} {\n${cssVariables}\n}`;
735
-
736
- // Inject into DOM if requested
737
- if (inject && isBrowser()) {
738
- injectCSS(css, styleId);
739
- }
740
-
741
- return css;
735
+ const {
736
+ selector = ':root',
737
+ inject = false,
738
+ styleId = 'atomix-theme-variables',
739
+ prefix = 'atomix',
740
+ } = options;
741
+
742
+ const variables: Record<string, string> = {};
743
+
744
+ // Generate variables from each theme section
745
+ Object.assign(variables, generatePaletteVariables(theme.palette, prefix));
746
+ Object.assign(variables, generateTypographyVariables(theme.typography, prefix));
747
+ Object.assign(variables, generateShadowVariables(theme.shadows, prefix));
748
+ Object.assign(variables, generateTransitionVariables(theme.transitions, prefix));
749
+ Object.assign(variables, generateZIndexVariables(theme.zIndex, prefix));
750
+ Object.assign(variables, generateBreakpointVariables(theme.breakpoints, prefix));
751
+ Object.assign(variables, generateSpacingVariables(theme.spacing, prefix));
752
+ Object.assign(variables, generateBorderVariables(theme.palette, prefix));
753
+ Object.assign(variables, generateBorderRadiusVariables(theme.borderRadius, prefix));
754
+ Object.assign(variables, generateFocusRingVariables(theme.palette, prefix));
755
+
756
+ // Add custom properties if present
757
+ if (theme.custom && Object.keys(theme.custom).length > 0) {
758
+ const customVars = flattenObject(theme.custom, `${prefix}-custom`);
759
+ Object.assign(variables, customVars);
760
+ }
761
+
762
+ // Convert to CSS string
763
+ const cssVariables = Object.entries(variables)
764
+ .map(([key, value]) => ` --${key}: ${value};`)
765
+ .join('\n');
766
+
767
+ const css = `${selector} {\n${cssVariables}\n}`;
768
+
769
+ // Inject into DOM if requested
770
+ if (inject && isBrowser()) {
771
+ injectCSS(css, styleId);
772
+ }
773
+
774
+ return css;
742
775
  }
743
776
 
744
777
  /**
745
778
  * Inject CSS into the DOM
746
- *
779
+ *
747
780
  * @param css - CSS string to inject
748
781
  * @param styleId - ID for the style element
749
782
  */
750
783
  export function injectCSS(css: string, styleId: string = 'atomix-theme-variables'): void {
751
- if (!isBrowser()) return;
784
+ if (!isBrowser()) return;
752
785
 
753
- let styleElement = document.getElementById(styleId) as HTMLStyleElement | null;
786
+ let styleElement = document.getElementById(styleId) as HTMLStyleElement | null;
754
787
 
755
- if (!styleElement) {
756
- styleElement = document.createElement('style');
757
- styleElement.id = styleId;
758
- styleElement.setAttribute('data-atomix-theme-vars', 'true');
759
- document.head.appendChild(styleElement);
760
- }
788
+ if (!styleElement) {
789
+ styleElement = document.createElement('style');
790
+ styleElement.id = styleId;
791
+ styleElement.setAttribute('data-atomix-theme-vars', 'true');
792
+ document.head.appendChild(styleElement);
793
+ }
761
794
 
762
- styleElement.textContent = css;
795
+ styleElement.textContent = css;
763
796
  }
764
797
 
765
798
  /**
766
799
  * Remove injected CSS from the DOM
767
- *
800
+ *
768
801
  * @param styleId - ID of the style element to remove
769
802
  */
770
803
  export function removeInjectedCSS(styleId: string = 'atomix-theme-variables'): void {
771
- if (!isBrowser()) return;
804
+ if (!isBrowser()) return;
772
805
 
773
- const styleElement = document.getElementById(styleId);
774
- if (styleElement) {
775
- styleElement.remove();
776
- }
806
+ const styleElement = document.getElementById(styleId);
807
+ if (styleElement) {
808
+ styleElement.remove();
809
+ }
777
810
  }
778
811
 
779
812
  /**
780
813
  * Generate CSS variables for a specific theme section
781
- *
814
+ *
782
815
  * @param theme - Theme object
783
816
  * @param section - Theme section to generate variables for
784
817
  * @param options - Generation options
785
818
  * @returns CSS string with custom properties for the section
786
819
  */
787
820
  export function generateSectionVariables(
788
- theme: Theme,
789
- section: 'palette' | 'typography' | 'shadows' | 'transitions' | 'zIndex' | 'breakpoints' | 'spacing' | 'borders' | 'borderRadius' | 'focusRing',
790
- options: GenerateCSSVariablesOptions = {}
821
+ theme: Theme,
822
+ section:
823
+ | 'palette'
824
+ | 'typography'
825
+ | 'shadows'
826
+ | 'transitions'
827
+ | 'zIndex'
828
+ | 'breakpoints'
829
+ | 'spacing'
830
+ | 'borders'
831
+ | 'borderRadius'
832
+ | 'focusRing',
833
+ options: GenerateCSSVariablesOptions = {}
791
834
  ): string {
792
- const { selector = ':root', prefix = 'atomix' } = options;
793
-
794
- let variables: Record<string, string> = {};
795
-
796
- switch (section) {
797
- case 'palette':
798
- variables = generatePaletteVariables(theme.palette, prefix);
799
- break;
800
- case 'typography':
801
- variables = generateTypographyVariables(theme.typography, prefix);
802
- break;
803
- case 'shadows':
804
- variables = generateShadowVariables(theme.shadows, prefix);
805
- break;
806
- case 'transitions':
807
- variables = generateTransitionVariables(theme.transitions, prefix);
808
- break;
809
- case 'zIndex':
810
- variables = generateZIndexVariables(theme.zIndex, prefix);
811
- break;
812
- case 'breakpoints':
813
- variables = generateBreakpointVariables(theme.breakpoints, prefix);
814
- break;
815
- case 'spacing':
816
- variables = generateSpacingVariables(theme.spacing, prefix);
817
- break;
818
- case 'borders':
819
- variables = generateBorderVariables(theme.palette, prefix);
820
- break;
821
- case 'borderRadius':
822
- variables = generateBorderRadiusVariables(theme.borderRadius, prefix);
823
- break;
824
- case 'focusRing':
825
- variables = generateFocusRingVariables(theme.palette, prefix);
826
- break;
827
- }
828
-
829
- const cssVariables = Object.entries(variables)
830
- .map(([key, value]) => ` --${key}: ${value};`)
831
- .join('\n');
832
-
833
- return `${selector} {\n${cssVariables}\n}`;
835
+ const { selector = ':root', prefix = 'atomix' } = options;
836
+
837
+ let variables: Record<string, string> = {};
838
+
839
+ switch (section) {
840
+ case 'palette':
841
+ variables = generatePaletteVariables(theme.palette, prefix);
842
+ break;
843
+ case 'typography':
844
+ variables = generateTypographyVariables(theme.typography, prefix);
845
+ break;
846
+ case 'shadows':
847
+ variables = generateShadowVariables(theme.shadows, prefix);
848
+ break;
849
+ case 'transitions':
850
+ variables = generateTransitionVariables(theme.transitions, prefix);
851
+ break;
852
+ case 'zIndex':
853
+ variables = generateZIndexVariables(theme.zIndex, prefix);
854
+ break;
855
+ case 'breakpoints':
856
+ variables = generateBreakpointVariables(theme.breakpoints, prefix);
857
+ break;
858
+ case 'spacing':
859
+ variables = generateSpacingVariables(theme.spacing, prefix);
860
+ break;
861
+ case 'borders':
862
+ variables = generateBorderVariables(theme.palette, prefix);
863
+ break;
864
+ case 'borderRadius':
865
+ variables = generateBorderRadiusVariables(theme.borderRadius, prefix);
866
+ break;
867
+ case 'focusRing':
868
+ variables = generateFocusRingVariables(theme.palette, prefix);
869
+ break;
870
+ }
871
+
872
+ const cssVariables = Object.entries(variables)
873
+ .map(([key, value]) => ` --${key}: ${value};`)
874
+ .join('\n');
875
+
876
+ return `${selector} {\n${cssVariables}\n}`;
834
877
  }
835
878
 
836
879
  export default generateCSSVariables;