@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.
- package/build-tools/index.d.ts +31 -30
- package/build-tools/package.json +4 -21
- package/dist/atomix.css +20924 -2611
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +76 -2
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/index.d.ts +31 -30
- package/dist/build-tools/package.json +4 -21
- package/dist/charts.js.map +1 -1
- package/dist/core.js.map +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +144 -18
- package/dist/index.esm.js +110 -55
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +110 -55
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js.map +1 -1
- package/dist/theme.d.ts +9 -9
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.stories.tsx +32 -23
- package/src/components/Accordion/Accordion.test.tsx +70 -50
- package/src/components/Accordion/Accordion.tsx +99 -94
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
- package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
- package/src/components/AtomixGlass/glass-utils.ts +4 -3
- package/src/components/AtomixGlass/shader-utils.ts +128 -52
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +1 -1
- package/src/components/Avatar/Avatar.stories.tsx +45 -62
- package/src/components/Avatar/Avatar.tsx +58 -56
- package/src/components/Badge/Badge.stories.tsx +20 -9
- package/src/components/Badge/Badge.test.tsx +41 -41
- package/src/components/Badge/Badge.tsx +64 -62
- package/src/components/Block/Block.stories.tsx +14 -4
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +9 -8
- package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
- package/src/components/Button/Button.stories.tsx +13 -22
- package/src/components/Button/Button.test.tsx +97 -81
- package/src/components/Button/Button.tsx +46 -14
- package/src/components/Button/ButtonGroup.stories.tsx +37 -32
- package/src/components/Button/ButtonGroup.tsx +4 -15
- package/src/components/Callout/Callout.stories.tsx +109 -16
- package/src/components/Card/Card.stories.tsx +67 -36
- package/src/components/Card/Card.tsx +30 -14
- package/src/components/Chart/AreaChart.tsx +1 -1
- package/src/components/Chart/CandlestickChart.tsx +23 -16
- package/src/components/Chart/Chart.stories.tsx +4 -9
- package/src/components/Chart/Chart.tsx +40 -44
- package/src/components/Chart/ChartRenderer.tsx +39 -12
- package/src/components/Chart/ChartToolbar.tsx +21 -5
- package/src/components/Chart/DonutChart.tsx +1 -1
- package/src/components/Chart/FunnelChart.tsx +4 -1
- package/src/components/Chart/GaugeChart.tsx +3 -1
- package/src/components/Chart/HeatmapChart.tsx +50 -37
- package/src/components/Chart/LineChart.tsx +3 -2
- package/src/components/Chart/MultiAxisChart.tsx +24 -16
- package/src/components/Chart/RadarChart.tsx +19 -17
- package/src/components/Chart/ScatterChart.tsx +29 -21
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -2
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
- package/src/components/Countdown/Countdown.stories.tsx +7 -7
- package/src/components/DataTable/DataTable.stories.tsx +43 -38
- package/src/components/DataTable/DataTable.test.tsx +26 -148
- package/src/components/DataTable/DataTable.tsx +485 -456
- package/src/components/DatePicker/DatePicker.stories.tsx +32 -47
- package/src/components/DatePicker/DatePicker.tsx +31 -26
- package/src/components/Dropdown/Dropdown.stories.tsx +2 -5
- package/src/components/Dropdown/Dropdown.tsx +313 -299
- package/src/components/EdgePanel/EdgePanel.stories.tsx +6 -19
- package/src/components/EdgePanel/EdgePanel.tsx +1 -3
- package/src/components/Footer/Footer.stories.tsx +21 -16
- package/src/components/Footer/Footer.tsx +130 -128
- package/src/components/Footer/FooterLink.tsx +2 -2
- package/src/components/Form/Checkbox.test.tsx +49 -49
- package/src/components/Form/Checkbox.tsx +108 -100
- package/src/components/Form/Form.stories.tsx +2 -10
- package/src/components/Form/Input.stories.tsx +22 -39
- package/src/components/Form/Input.test.tsx +38 -44
- package/src/components/Form/Radio.stories.tsx +6 -12
- package/src/components/Form/Radio.tsx +68 -66
- package/src/components/Form/Select.tsx +184 -182
- package/src/components/Form/Textarea.test.tsx +27 -32
- package/src/components/Hero/Hero.stories.tsx +56 -23
- package/src/components/Hero/Hero.tsx +201 -55
- package/src/components/Icon/index.ts +7 -1
- package/src/components/List/List.tsx +19 -23
- package/src/components/Modal/Modal.stories.tsx +2 -1
- package/src/components/Modal/Modal.tsx +130 -127
- package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
- package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
- package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
- package/src/components/Pagination/Pagination.stories.tsx +7 -4
- package/src/components/Pagination/Pagination.tsx +199 -202
- package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
- package/src/components/Popover/Popover.stories.tsx +99 -192
- package/src/components/Popover/Popover.tsx +41 -37
- package/src/components/Progress/Progress.stories.tsx +35 -44
- package/src/components/River/River.stories.tsx +2 -1
- package/src/components/SectionIntro/SectionIntro.stories.tsx +71 -71
- package/src/components/Slider/Slider.stories.tsx +12 -4
- package/src/components/Spinner/Spinner.stories.tsx +3 -1
- package/src/components/Spinner/Spinner.test.tsx +23 -23
- package/src/components/Spinner/Spinner.tsx +43 -46
- package/src/components/Steps/Steps.stories.tsx +8 -6
- package/src/components/Tabs/Tabs.stories.tsx +12 -9
- package/src/components/Tabs/Tabs.tsx +74 -72
- package/src/components/Toggle/Toggle.stories.tsx +27 -13
- package/src/components/Toggle/Toggle.test.tsx +65 -70
- package/src/components/Toggle/Toggle.tsx +4 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +24 -20
- package/src/components/Tooltip/Tooltip.tsx +104 -106
- package/src/components/Upload/Upload.stories.tsx +129 -127
- package/src/components/Upload/Upload.tsx +287 -283
- package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
- package/src/components/index.ts +13 -2
- package/src/layouts/Grid/Grid.stories.tsx +9 -3
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
- package/src/lib/__tests__/theme-tools.test.ts +32 -6
- package/src/lib/composables/shared-mouse-tracker.ts +13 -14
- package/src/lib/composables/useAtomixGlass.ts +106 -49
- package/src/lib/composables/useChartExport.ts +1 -1
- package/src/lib/composables/useDataTable.ts +29 -17
- package/src/lib/composables/useHero.ts +58 -14
- package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
- package/src/lib/composables/useInput.ts +10 -8
- package/src/lib/composables/useSideMenu.ts +6 -5
- package/src/lib/composables/useTooltip.ts +1 -2
- package/src/lib/composables/useVideoPlayer.ts +44 -35
- package/src/lib/config/index.ts +154 -154
- package/src/lib/constants/cssVariables.ts +29 -29
- package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
- package/src/lib/hooks/index.ts +1 -1
- package/src/lib/hooks/useComponentCustomization.ts +11 -17
- package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
- package/src/lib/patterns/__tests__/slots.test.ts +1 -1
- package/src/lib/patterns/index.ts +1 -1
- package/src/lib/patterns/slots.tsx +8 -13
- package/src/lib/storybook/InteractiveDemo.tsx +13 -18
- package/src/lib/storybook/PreviewContainer.tsx +1 -1
- package/src/lib/storybook/VariantsGrid.tsx +3 -7
- package/src/lib/storybook/index.ts +1 -1
- package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
- package/src/lib/theme/adapters/index.ts +3 -9
- package/src/lib/theme/adapters/themeAdapter.ts +41 -26
- package/src/lib/theme/config/index.ts +1 -1
- package/src/lib/theme/config/types.ts +2 -2
- package/src/lib/theme/config/validator.ts +10 -5
- package/src/lib/theme/constants/constants.ts +2 -2
- package/src/lib/theme/constants/index.ts +1 -2
- package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
- package/src/lib/theme/core/composeTheme.ts +32 -26
- package/src/lib/theme/core/createTheme.ts +1 -1
- package/src/lib/theme/core/createThemeObject.ts +308 -301
- package/src/lib/theme/core/index.ts +3 -3
- package/src/lib/theme/devtools/CLI.ts +106 -104
- package/src/lib/theme/devtools/Comparator.tsx +50 -32
- package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
- package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
- package/src/lib/theme/devtools/Inspector.tsx +75 -60
- package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
- package/src/lib/theme/devtools/Preview.tsx +150 -106
- package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
- package/src/lib/theme/devtools/index.ts +3 -9
- package/src/lib/theme/devtools/useHistory.ts +23 -21
- package/src/lib/theme/errors/errors.ts +12 -11
- package/src/lib/theme/errors/index.ts +2 -7
- package/src/lib/theme/generators/generateCSS.ts +9 -13
- package/src/lib/theme/generators/generateCSSNested.ts +1 -6
- package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
- package/src/lib/theme/generators/index.ts +1 -4
- package/src/lib/theme/i18n/index.ts +1 -1
- package/src/lib/theme/i18n/rtl.ts +13 -13
- package/src/lib/theme/index.ts +7 -16
- package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
- package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
- package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
- package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
- package/src/lib/theme/runtime/index.ts +2 -5
- package/src/lib/theme/runtime/useTheme.ts +18 -18
- package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
- package/src/lib/theme/test/testTheme.ts +15 -16
- package/src/lib/theme/tokens/index.ts +2 -7
- package/src/lib/theme/tokens/tokens.ts +25 -24
- package/src/lib/theme/types.ts +428 -411
- package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
- package/src/lib/theme/utils/componentTheming.ts +18 -18
- package/src/lib/theme/utils/domUtils.ts +277 -289
- package/src/lib/theme/utils/index.ts +1 -2
- package/src/lib/theme/utils/injectCSS.ts +10 -14
- package/src/lib/theme/utils/naming.ts +20 -16
- package/src/lib/theme/utils/themeHelpers.ts +10 -12
- package/src/lib/theme/utils/themeUtils.ts +85 -86
- package/src/lib/theme/utils/themeValidation.ts +82 -33
- package/src/lib/theme-tools.ts +8 -6
- package/src/lib/types/components.ts +172 -71
- package/src/lib/types/partProps.ts +1 -1
- package/src/lib/utils/__tests__/csv.test.ts +1 -1
- package/src/lib/utils/componentUtils.ts +8 -12
- package/src/lib/utils/csv.ts +3 -1
- package/src/lib/utils/dataTableExport.ts +1 -5
- package/src/lib/utils/fontPreloader.ts +10 -19
- package/src/lib/utils/icons.ts +4 -1
- package/src/lib/utils/index.ts +2 -6
- package/src/lib/utils/memoryMonitor.ts +10 -8
- package/src/lib/utils/themeNaming.ts +2 -2
- package/src/styles/01-settings/_index.scss +0 -1
- package/src/styles/01-settings/_settings.colors.scss +8 -8
- package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
- package/src/styles/01-settings/_settings.navbar.scss +1 -1
- package/src/styles/01-settings/_settings.spacing.scss +3 -4
- package/src/styles/01-settings/_settings.tooltip.scss +1 -1
- package/src/styles/01-settings/_settings.typography.scss +1 -1
- package/src/styles/02-tools/_tools.button.scss +51 -21
- package/src/styles/02-tools/_tools.utility-api.scss +30 -18
- package/src/styles/03-generic/_generic.root.scss +4 -3
- package/src/styles/06-components/_components.atomix-glass.scss +13 -9
- package/src/styles/06-components/_components.button.scss +16 -4
- package/src/styles/06-components/_components.callout.scss +27 -21
- package/src/styles/06-components/_components.card.scss +5 -14
- package/src/styles/06-components/_components.chart.scss +22 -19
- package/src/styles/06-components/_components.checkbox.scss +3 -1
- package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
- package/src/styles/06-components/_components.edge-panel.scss +9 -2
- package/src/styles/06-components/_components.footer.scss +1 -1
- package/src/styles/06-components/_components.side-menu.scss +5 -5
- package/src/styles/06-components/_components.toggle.scss +18 -0
- package/src/styles/06-components/_index.scss +1 -1
- package/src/styles/06-components/old.chart.styles.scss +0 -2
- package/src/styles/99-utilities/_utilities.border.scss +69 -27
- package/src/styles/99-utilities/_utilities.display.scss +1 -1
- package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
- package/src/styles/99-utilities/_utilities.position.scss +16 -9
- package/src/styles/99-utilities/_utilities.scss +1 -1
- package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
- package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
- package/src/styles/99-utilities/_utilities.text-gradient.scss +30 -30
- 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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
obj: Record<string, any>,
|
|
47
|
+
prefix: string = '',
|
|
48
|
+
result: Record<string, string> = {}
|
|
49
49
|
): Record<string, string> {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
|
226
|
+
// Brand bg subtle (uses primary color with alpha)
|
|
241
227
|
if (palette.primary) {
|
|
242
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
//
|
|
261
|
-
if (palette.primary) {
|
|
262
|
-
|
|
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
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
289
|
-
if (palette.
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
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
|
-
|
|
348
|
-
|
|
374
|
+
typography: Theme['typography'],
|
|
375
|
+
prefix: string
|
|
349
376
|
): Record<string, string> {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
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(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
|
|
461
|
-
|
|
492
|
+
transitions: Theme['transitions'],
|
|
493
|
+
prefix: string
|
|
462
494
|
): Record<string, string> {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
-
|
|
527
|
-
|
|
559
|
+
breakpoints: Theme['breakpoints'],
|
|
560
|
+
prefix: string
|
|
528
561
|
): Record<string, string> {
|
|
529
|
-
|
|
562
|
+
const vars: Record<string, string> = {};
|
|
530
563
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
564
|
+
Object.entries(breakpoints.values).forEach(([key, value]) => {
|
|
565
|
+
vars[`${prefix}-breakpoint-${key}`] = `${value}${breakpoints.unit}`;
|
|
566
|
+
});
|
|
534
567
|
|
|
535
|
-
|
|
568
|
+
return vars;
|
|
536
569
|
}
|
|
537
570
|
|
|
538
571
|
/**
|
|
539
572
|
* Generate CSS variables from theme spacing
|
|
540
573
|
*/
|
|
541
574
|
function generateSpacingVariables(
|
|
542
|
-
|
|
543
|
-
|
|
575
|
+
spacing: Theme['spacing'],
|
|
576
|
+
prefix: string
|
|
544
577
|
): Record<string, string> {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
613
|
-
|
|
645
|
+
palette: Theme['palette'],
|
|
646
|
+
prefix: string
|
|
614
647
|
): Record<string, string> {
|
|
615
|
-
|
|
648
|
+
const vars: Record<string, string> = {};
|
|
616
649
|
|
|
617
|
-
|
|
618
|
-
|
|
650
|
+
// Border width
|
|
651
|
+
vars[`${prefix}-border-width`] = '1px';
|
|
619
652
|
|
|
620
|
-
|
|
621
|
-
|
|
653
|
+
// Border style
|
|
654
|
+
vars[`${prefix}-border-style`] = 'solid';
|
|
622
655
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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
|
-
|
|
661
|
+
return vars;
|
|
629
662
|
}
|
|
630
663
|
|
|
631
664
|
/**
|
|
632
665
|
* Generate border radius CSS variables
|
|
633
666
|
*/
|
|
634
667
|
function generateBorderRadiusVariables(
|
|
635
|
-
|
|
636
|
-
|
|
668
|
+
borderRadius: Theme['borderRadius'],
|
|
669
|
+
prefix: string
|
|
637
670
|
): Record<string, string> {
|
|
638
|
-
|
|
671
|
+
const vars: Record<string, string> = {};
|
|
639
672
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
|
|
648
|
-
|
|
680
|
+
// Base border radius (maps to spacing-2 = 8px)
|
|
681
|
+
vars[`${prefix}-border-radius`] = formatValue(borderRadius.base, '0.5rem');
|
|
649
682
|
|
|
650
|
-
|
|
651
|
-
|
|
683
|
+
// Small border radius (maps to spacing-1 = 4px)
|
|
684
|
+
vars[`${prefix}-border-radius-sm`] = formatValue(borderRadius.sm, '0.25rem');
|
|
652
685
|
|
|
653
|
-
|
|
654
|
-
|
|
686
|
+
// Large border radius (maps to spacing-2.5 = 10px)
|
|
687
|
+
vars[`${prefix}-border-radius-lg`] = formatValue(borderRadius.lg, '0.625rem');
|
|
655
688
|
|
|
656
|
-
|
|
657
|
-
|
|
689
|
+
// Extra large border radius (maps to spacing-3 = 12px)
|
|
690
|
+
vars[`${prefix}-border-radius-xl`] = formatValue(borderRadius.xl, '0.75rem');
|
|
658
691
|
|
|
659
|
-
|
|
660
|
-
|
|
692
|
+
// 2X large border radius (maps to spacing-4 = 16px)
|
|
693
|
+
vars[`${prefix}-border-radius-xxl`] = formatValue(borderRadius.xxl, '1rem');
|
|
661
694
|
|
|
662
|
-
|
|
663
|
-
|
|
695
|
+
// 3X large border radius (maps to spacing-6 = 24px)
|
|
696
|
+
vars[`${prefix}-border-radius-3xl`] = formatValue(borderRadius['3xl'], '1.5rem');
|
|
664
697
|
|
|
665
|
-
|
|
666
|
-
|
|
698
|
+
// 4X large border radius (maps to spacing-8 = 32px)
|
|
699
|
+
vars[`${prefix}-border-radius-4xl`] = formatValue(borderRadius['4xl'], '2rem');
|
|
667
700
|
|
|
668
|
-
|
|
669
|
-
|
|
701
|
+
// Pill shape (fully rounded, maps to spacing-200 = 800px)
|
|
702
|
+
vars[`${prefix}-border-radius-pill`] = formatValue(borderRadius.pill, '50rem');
|
|
670
703
|
|
|
671
|
-
|
|
704
|
+
return vars;
|
|
672
705
|
}
|
|
673
706
|
|
|
674
707
|
/**
|
|
675
708
|
* Generate focus ring CSS variables
|
|
676
709
|
*/
|
|
677
710
|
function generateFocusRingVariables(
|
|
678
|
-
|
|
679
|
-
|
|
711
|
+
palette: Theme['palette'],
|
|
712
|
+
prefix: string
|
|
680
713
|
): Record<string, string> {
|
|
681
|
-
|
|
714
|
+
const vars: Record<string, string> = {};
|
|
682
715
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
-
|
|
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
|
-
|
|
700
|
-
|
|
732
|
+
theme: Theme,
|
|
733
|
+
options: GenerateCSSVariablesOptions = {}
|
|
701
734
|
): string {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
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
|
-
|
|
784
|
+
if (!isBrowser()) return;
|
|
752
785
|
|
|
753
|
-
|
|
786
|
+
let styleElement = document.getElementById(styleId) as HTMLStyleElement | null;
|
|
754
787
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
-
|
|
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
|
-
|
|
804
|
+
if (!isBrowser()) return;
|
|
772
805
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
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
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
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;
|