@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,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CSS Injection Utilities
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Inject CSS into HTML head via <style> element.
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -13,26 +13,23 @@ function isBrowser(): boolean {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Inject CSS into HTML head via <style> element
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
17
|
* Creates or updates a style element in the document head.
|
|
18
18
|
* If an element with the same ID exists, it will be updated.
|
|
19
|
-
*
|
|
19
|
+
*
|
|
20
20
|
* @param css - CSS string to inject
|
|
21
21
|
* @param id - Style element ID (default: 'atomix-theme')
|
|
22
|
-
*
|
|
22
|
+
*
|
|
23
23
|
* @example
|
|
24
24
|
* ```typescript
|
|
25
25
|
* const css = ':root { --atomix-color-primary: #7AFFD7; }';
|
|
26
26
|
* injectCSS(css);
|
|
27
|
-
*
|
|
27
|
+
*
|
|
28
28
|
* // With custom ID
|
|
29
29
|
* injectCSS(css, 'my-custom-theme');
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
export function injectCSS(
|
|
33
|
-
css: string,
|
|
34
|
-
id: string = 'atomix-theme'
|
|
35
|
-
): void {
|
|
32
|
+
export function injectCSS(css: string, id: string = 'atomix-theme'): void {
|
|
36
33
|
if (!isBrowser()) {
|
|
37
34
|
return;
|
|
38
35
|
}
|
|
@@ -51,11 +48,11 @@ export function injectCSS(
|
|
|
51
48
|
|
|
52
49
|
/**
|
|
53
50
|
* Remove injected CSS from DOM
|
|
54
|
-
*
|
|
51
|
+
*
|
|
55
52
|
* Removes the style element with the given ID from the document head.
|
|
56
|
-
*
|
|
53
|
+
*
|
|
57
54
|
* @param id - Style element ID to remove (default: 'atomix-theme')
|
|
58
|
-
*
|
|
55
|
+
*
|
|
59
56
|
* @example
|
|
60
57
|
* ```typescript
|
|
61
58
|
* removeCSS(); // Removes default 'atomix-theme'
|
|
@@ -75,7 +72,7 @@ export function removeCSS(id: string = 'atomix-theme'): void {
|
|
|
75
72
|
|
|
76
73
|
/**
|
|
77
74
|
* Check if CSS is already injected
|
|
78
|
-
*
|
|
75
|
+
*
|
|
79
76
|
* @param id - Style element ID to check (default: 'atomix-theme')
|
|
80
77
|
* @returns True if style element exists
|
|
81
78
|
*/
|
|
@@ -86,4 +83,3 @@ export function isCSSInjected(id: string = 'atomix-theme'): boolean {
|
|
|
86
83
|
|
|
87
84
|
return document.getElementById(id) !== null;
|
|
88
85
|
}
|
|
89
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Naming Utilities
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Provides consistent naming conventions across the theme system
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -14,13 +14,17 @@ export interface NamingOptions {
|
|
|
14
14
|
/**
|
|
15
15
|
* Generate consistent CSS class names following BEM methodology
|
|
16
16
|
*/
|
|
17
|
-
export function generateClassName(
|
|
17
|
+
export function generateClassName(
|
|
18
|
+
block: string,
|
|
19
|
+
element?: string,
|
|
20
|
+
modifiers?: Record<string, boolean | string>
|
|
21
|
+
): string {
|
|
18
22
|
let className = block;
|
|
19
|
-
|
|
23
|
+
|
|
20
24
|
if (element) {
|
|
21
25
|
className += `__${element}`;
|
|
22
26
|
}
|
|
23
|
-
|
|
27
|
+
|
|
24
28
|
if (modifiers) {
|
|
25
29
|
Object.entries(modifiers).forEach(([key, value]) => {
|
|
26
30
|
if (value) {
|
|
@@ -31,7 +35,7 @@ export function generateClassName(block: string, element?: string, modifiers?: R
|
|
|
31
35
|
}
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
|
-
|
|
38
|
+
|
|
35
39
|
return className;
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -40,23 +44,23 @@ export function generateClassName(block: string, element?: string, modifiers?: R
|
|
|
40
44
|
*/
|
|
41
45
|
export function generateCSSVariableName(property: string, options: NamingOptions = {}): string {
|
|
42
46
|
const { prefix = 'atomix', component, variant, state } = options;
|
|
43
|
-
|
|
47
|
+
|
|
44
48
|
const parts = [prefix];
|
|
45
|
-
|
|
49
|
+
|
|
46
50
|
if (component) {
|
|
47
51
|
parts.push(component);
|
|
48
52
|
}
|
|
49
|
-
|
|
53
|
+
|
|
50
54
|
if (variant) {
|
|
51
55
|
parts.push(variant);
|
|
52
56
|
}
|
|
53
|
-
|
|
57
|
+
|
|
54
58
|
if (state) {
|
|
55
59
|
parts.push(state);
|
|
56
60
|
}
|
|
57
|
-
|
|
61
|
+
|
|
58
62
|
parts.push(property);
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
return `--${parts.join('-')}`;
|
|
61
65
|
}
|
|
62
66
|
|
|
@@ -65,7 +69,7 @@ export function generateCSSVariableName(property: string, options: NamingOptions
|
|
|
65
69
|
*/
|
|
66
70
|
export function normalizeThemeTokens(tokens: Record<string, any>): Record<string, any> {
|
|
67
71
|
const normalized: Record<string, any> = {};
|
|
68
|
-
|
|
72
|
+
|
|
69
73
|
for (const [key, value] of Object.entries(tokens)) {
|
|
70
74
|
if (typeof value === 'object' && value !== null) {
|
|
71
75
|
// Recursively normalize nested objects
|
|
@@ -75,7 +79,7 @@ export function normalizeThemeTokens(tokens: Record<string, any>): Record<string
|
|
|
75
79
|
normalized[key] = value;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
|
-
|
|
82
|
+
|
|
79
83
|
return normalized;
|
|
80
84
|
}
|
|
81
85
|
|
|
@@ -83,7 +87,7 @@ export function normalizeThemeTokens(tokens: Record<string, any>): Record<string
|
|
|
83
87
|
* Convert camelCase to kebab-case for CSS custom properties
|
|
84
88
|
*/
|
|
85
89
|
export function camelToKebab(str: string): string {
|
|
86
|
-
return str.replace(/[A-Z]/g,
|
|
90
|
+
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
/**
|
|
@@ -95,6 +99,6 @@ export function themePropertyToCSSVar(propertyPath: string, prefix: string = 'at
|
|
|
95
99
|
.split('.')
|
|
96
100
|
.map(part => camelToKebab(part))
|
|
97
101
|
.join('-');
|
|
98
|
-
|
|
102
|
+
|
|
99
103
|
return `--${prefix}-${path}`;
|
|
100
|
-
}
|
|
104
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme Helper Functions
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Utility functions for working with DesignTokens
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -8,33 +8,31 @@ import type { DesignTokens } from '../tokens/tokens';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Check if a value is DesignTokens
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* Type guard to check if an object is DesignTokens format.
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* @param value - Value to check
|
|
15
15
|
* @returns True if value is DesignTokens
|
|
16
16
|
*/
|
|
17
17
|
export function isDesignTokens(value: unknown): value is DesignTokens {
|
|
18
18
|
if (!value || typeof value !== 'object') return false;
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
// DesignTokens is a flat object with string keys, no nested structures
|
|
21
21
|
const obj = value as Record<string, unknown>;
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
// Check for absence of Theme-specific properties
|
|
24
24
|
if ('palette' in obj || 'typography' in obj || '__isJSTheme' in obj) {
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// Check if it has DesignTokens-like structure (flat string keys)
|
|
29
29
|
const keys = Object.keys(obj);
|
|
30
30
|
if (keys.length === 0) return false;
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
// Check if keys look like DesignTokens (kebab-case, no nesting)
|
|
33
|
-
const hasDesignTokenKeys = keys.some(
|
|
34
|
-
/^[a-z]+(-[a-z0-9]+)*$/.test(key) &&
|
|
35
|
-
typeof obj[key] === 'string'
|
|
33
|
+
const hasDesignTokenKeys = keys.some(
|
|
34
|
+
key => /^[a-z]+(-[a-z0-9]+)*$/.test(key) && typeof obj[key] === 'string'
|
|
36
35
|
);
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
return hasDesignTokenKeys;
|
|
39
38
|
}
|
|
40
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme Utilities
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Helper utilities for working with themes, including color manipulation,
|
|
5
5
|
* spacing helpers, and theme value accessors.
|
|
6
6
|
*/
|
|
@@ -15,27 +15,27 @@ import type { SpacingFunction, SpacingOptions } from '../types';
|
|
|
15
15
|
* Convert hex color to RGB object
|
|
16
16
|
*/
|
|
17
17
|
export function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
19
|
+
return result
|
|
20
|
+
? {
|
|
21
|
+
r: parseInt(result[1]!, 16),
|
|
22
|
+
g: parseInt(result[2]!, 16),
|
|
23
|
+
b: parseInt(result[3]!, 16),
|
|
24
|
+
}
|
|
25
|
+
: null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Convert RGB to hex color
|
|
30
30
|
*/
|
|
31
31
|
export function rgbToHex(r: number, g: number, b: number): string {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const toHex = (val: number) => {
|
|
33
|
+
const hex = Math.round(Math.max(0, Math.min(255, val)))
|
|
34
|
+
.toString(16)
|
|
35
|
+
.padStart(2, '0');
|
|
36
|
+
return hex;
|
|
37
|
+
};
|
|
38
|
+
return `#${toHex(r ?? 0)}${toHex(g ?? 0)}${toHex(b ?? 0)}`;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -43,106 +43,106 @@ export function rgbToHex(r: number, g: number, b: number): string {
|
|
|
43
43
|
* Used for determining contrast ratios
|
|
44
44
|
*/
|
|
45
45
|
export function getLuminance(color: string): number {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const rgb = hexToRgb(color);
|
|
47
|
+
if (!rgb) return 0;
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const { r, g, b } = rgb;
|
|
50
|
+
const [rs, gs, bs] = [r ?? 0, g ?? 0, b ?? 0].map(c => {
|
|
51
|
+
const val = c / 255;
|
|
52
|
+
return val <= 0.03928 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
|
|
53
|
+
});
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
return 0.2126 * (rs ?? 0) + 0.7152 * (gs ?? 0) + 0.0722 * (bs ?? 0);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Calculate contrast ratio between two colors
|
|
60
60
|
*/
|
|
61
61
|
export function getContrastRatio(foreground: string, background: string): number {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
const lumA = getLuminance(foreground);
|
|
63
|
+
const lumB = getLuminance(background);
|
|
64
|
+
return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Get appropriate contrast text color (black or white) for a background color
|
|
69
69
|
*/
|
|
70
70
|
export function getContrastText(background: string, threshold: number = 3): string {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
const contrastWithWhite = getContrastRatio('#FFFFFF', background);
|
|
72
|
+
const contrastWithBlack = getContrastRatio('#000000', background);
|
|
73
|
+
|
|
74
|
+
if (contrastWithWhite >= threshold) {
|
|
75
|
+
return '#FFFFFF';
|
|
76
|
+
}
|
|
77
|
+
if (contrastWithBlack >= threshold) {
|
|
78
|
+
return '#000000';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Default to white if neither meets threshold
|
|
82
|
+
return contrastWithWhite > contrastWithBlack ? '#FFFFFF' : '#000000';
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Lighten a color by a given amount
|
|
87
|
-
*
|
|
87
|
+
*
|
|
88
88
|
* @param color - Hex color string
|
|
89
89
|
* @param amount - Amount to lighten (0-1), default 0.2
|
|
90
90
|
* @returns Lightened hex color
|
|
91
91
|
*/
|
|
92
92
|
export function lighten(color: string, amount: number = 0.2): string {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
const rgb = hexToRgb(color);
|
|
94
|
+
if (!rgb) return color;
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
const { r, g, b } = rgb;
|
|
97
|
+
const lightenValue = (val: number) => Math.min(255, Math.round(val + (255 - val) * amount));
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
return rgbToHex(lightenValue(r), lightenValue(g), lightenValue(b));
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* Darken a color by a given amount
|
|
104
|
-
*
|
|
104
|
+
*
|
|
105
105
|
* @param color - Hex color string
|
|
106
106
|
* @param amount - Amount to darken (0-1), default 0.2
|
|
107
107
|
* @returns Darkened hex color
|
|
108
108
|
*/
|
|
109
109
|
export function darken(color: string, amount: number = 0.2): string {
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
const rgb = hexToRgb(color);
|
|
111
|
+
if (!rgb) return color;
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
const { r, g, b } = rgb;
|
|
114
|
+
const darkenValue = (val: number) => Math.max(0, Math.round(val * (1 - amount)));
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
return rgbToHex(darkenValue(r), darkenValue(g), darkenValue(b));
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Add alpha (opacity) to a color
|
|
121
|
-
*
|
|
121
|
+
*
|
|
122
122
|
* @param color - Hex color string
|
|
123
123
|
* @param opacity - Opacity value (0-1)
|
|
124
124
|
* @returns RGBA color string
|
|
125
125
|
*/
|
|
126
126
|
export function alpha(color: string, opacity: number): string {
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const rgb = hexToRgb(color);
|
|
128
|
+
if (!rgb) return color;
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const { r, g, b } = rgb;
|
|
131
|
+
const clampedOpacity = Math.max(0, Math.min(1, opacity));
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Emphasize a color (lighten if dark, darken if light)
|
|
138
|
-
*
|
|
138
|
+
*
|
|
139
139
|
* @param color - Hex color string
|
|
140
140
|
* @param coefficient - Amount to emphasize (0-1), default 0.15
|
|
141
141
|
* @returns Emphasized hex color
|
|
142
142
|
*/
|
|
143
143
|
export function emphasize(color: string, coefficient: number = 0.15): string {
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
const luminance = getLuminance(color);
|
|
145
|
+
return luminance > 0.5 ? darken(color, coefficient) : lighten(color, coefficient);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// ============================================================================
|
|
@@ -151,36 +151,35 @@ export function emphasize(color: string, coefficient: number = 0.15): string {
|
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
153
|
* Create a spacing function from various input types
|
|
154
|
-
*
|
|
154
|
+
*
|
|
155
155
|
* @param spacingInput - Spacing configuration (number, array, or function), default 4
|
|
156
156
|
* @returns Spacing function
|
|
157
157
|
*/
|
|
158
158
|
export function createSpacing(spacingInput: SpacingOptions = 4): SpacingFunction {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return (...values: number[]) => {
|
|
167
|
-
if (values.length === 0) return '0px';
|
|
168
|
-
return values.map((value) => `${value * spacingInput}px`).join(' ');
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// If it's an array, use it as a scale
|
|
173
|
-
if (Array.isArray(spacingInput)) {
|
|
174
|
-
return (...values: number[]) => {
|
|
175
|
-
if (values.length === 0) return '0px';
|
|
176
|
-
return values.map((value) => `${spacingInput[value] || value}px`).join(' ');
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Default to 4px base
|
|
159
|
+
// If it's already a function, return it
|
|
160
|
+
if (typeof spacingInput === 'function') {
|
|
161
|
+
return spacingInput;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If it's a number, create a function that multiplies by that number
|
|
165
|
+
if (typeof spacingInput === 'number') {
|
|
181
166
|
return (...values: number[]) => {
|
|
182
|
-
|
|
183
|
-
|
|
167
|
+
if (values.length === 0) return '0px';
|
|
168
|
+
return values.map(value => `${value * spacingInput}px`).join(' ');
|
|
184
169
|
};
|
|
185
|
-
}
|
|
170
|
+
}
|
|
186
171
|
|
|
172
|
+
// If it's an array, use it as a scale
|
|
173
|
+
if (Array.isArray(spacingInput)) {
|
|
174
|
+
return (...values: number[]) => {
|
|
175
|
+
if (values.length === 0) return '0px';
|
|
176
|
+
return values.map(value => `${spacingInput[value] || value}px`).join(' ');
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Default to 4px base
|
|
181
|
+
return (...values: number[]) => {
|
|
182
|
+
if (values.length === 0) return '0px';
|
|
183
|
+
return values.map(value => `${value * 4}px`).join(' ');
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -154,9 +154,9 @@ function hslToRgb(hsl: string): { r: number; g: number; b: number } | null {
|
|
|
154
154
|
const hue2rgb = (p: number, q: number, t: number): number => {
|
|
155
155
|
if (t < 0) t += 1;
|
|
156
156
|
if (t > 1) t -= 1;
|
|
157
|
-
if (t < 1/6) return p + (q - p) * 6 * t;
|
|
158
|
-
if (t < 1/2) return q;
|
|
159
|
-
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|
157
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
158
|
+
if (t < 1 / 2) return q;
|
|
159
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
160
160
|
return p;
|
|
161
161
|
};
|
|
162
162
|
|
|
@@ -167,15 +167,15 @@ function hslToRgb(hsl: string): { r: number; g: number; b: number } | null {
|
|
|
167
167
|
} else {
|
|
168
168
|
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
169
169
|
const p = 2 * l - q;
|
|
170
|
-
r = hue2rgb(p, q, h + 1/3);
|
|
170
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
171
171
|
g = hue2rgb(p, q, h);
|
|
172
|
-
b = hue2rgb(p, q, h - 1/3);
|
|
172
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
return {
|
|
176
176
|
r: Math.round(r * 255),
|
|
177
177
|
g: Math.round(g * 255),
|
|
178
|
-
b: Math.round(b * 255)
|
|
178
|
+
b: Math.round(b * 255),
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
|
|
@@ -298,42 +298,87 @@ export function validateColorFormats(tokens: Partial<DesignTokens>): {
|
|
|
298
298
|
// Only validate tokens that are intended to be actual colors, not gradients, font weights, etc.
|
|
299
299
|
const colorTokenKeys = new Set([
|
|
300
300
|
// Base colors
|
|
301
|
-
'primary',
|
|
301
|
+
'primary',
|
|
302
|
+
'secondary',
|
|
303
|
+
'success',
|
|
304
|
+
'info',
|
|
305
|
+
'warning',
|
|
306
|
+
'error',
|
|
307
|
+
'light',
|
|
308
|
+
'dark',
|
|
302
309
|
// Text emphasis
|
|
303
|
-
'primary-text-emphasis',
|
|
304
|
-
'
|
|
305
|
-
'
|
|
306
|
-
'
|
|
310
|
+
'primary-text-emphasis',
|
|
311
|
+
'secondary-text-emphasis',
|
|
312
|
+
'tertiary-text-emphasis',
|
|
313
|
+
'disabled-text-emphasis',
|
|
314
|
+
'invert-text-emphasis',
|
|
315
|
+
'brand-text-emphasis',
|
|
316
|
+
'error-text-emphasis',
|
|
317
|
+
'success-text-emphasis',
|
|
318
|
+
'warning-text-emphasis',
|
|
319
|
+
'info-text-emphasis',
|
|
320
|
+
'light-text-emphasis',
|
|
321
|
+
'dark-text-emphasis',
|
|
307
322
|
// Background subtle
|
|
308
|
-
'primary-bg-subtle',
|
|
309
|
-
'
|
|
310
|
-
'
|
|
323
|
+
'primary-bg-subtle',
|
|
324
|
+
'secondary-bg-subtle',
|
|
325
|
+
'tertiary-bg-subtle',
|
|
326
|
+
'invert-bg-subtle',
|
|
327
|
+
'brand-bg-subtle',
|
|
328
|
+
'error-bg-subtle',
|
|
329
|
+
'success-bg-subtle',
|
|
330
|
+
'warning-bg-subtle',
|
|
331
|
+
'info-bg-subtle',
|
|
332
|
+
'light-bg-subtle',
|
|
333
|
+
'dark-bg-subtle',
|
|
311
334
|
// Border subtle
|
|
312
|
-
'primary-border-subtle',
|
|
313
|
-
'
|
|
314
|
-
'
|
|
335
|
+
'primary-border-subtle',
|
|
336
|
+
'secondary-border-subtle',
|
|
337
|
+
'success-border-subtle',
|
|
338
|
+
'error-border-subtle',
|
|
339
|
+
'warning-border-subtle',
|
|
340
|
+
'info-border-subtle',
|
|
341
|
+
'brand-border-subtle',
|
|
342
|
+
'light-border-subtle',
|
|
343
|
+
'dark-border-subtle',
|
|
315
344
|
// Hover states
|
|
316
|
-
'primary-hover',
|
|
317
|
-
'
|
|
345
|
+
'primary-hover',
|
|
346
|
+
'secondary-hover',
|
|
347
|
+
'light-hover',
|
|
348
|
+
'dark-hover',
|
|
349
|
+
'error-hover',
|
|
350
|
+
'success-hover',
|
|
351
|
+
'warning-hover',
|
|
352
|
+
'info-hover',
|
|
318
353
|
// Colors from scales (primary, red, green, blue, yellow)
|
|
319
354
|
...Array.from({ length: 10 }, (_, i) => [
|
|
320
|
-
`primary-${i + 1}`,
|
|
355
|
+
`primary-${i + 1}`,
|
|
356
|
+
`red-${i + 1}`,
|
|
357
|
+
`green-${i + 1}`,
|
|
358
|
+
`blue-${i + 1}`,
|
|
359
|
+
`yellow-${i + 1}`,
|
|
321
360
|
]).flat(),
|
|
322
361
|
// Gray scale
|
|
323
362
|
...Array.from({ length: 10 }, (_, i) => `gray-${i + 1}`),
|
|
324
363
|
// Body colors
|
|
325
|
-
'body-color',
|
|
364
|
+
'body-color',
|
|
365
|
+
'heading-color',
|
|
326
366
|
// Link colors
|
|
327
|
-
'link-color',
|
|
367
|
+
'link-color',
|
|
368
|
+
'link-hover-color',
|
|
328
369
|
// Highlight & code
|
|
329
|
-
'highlight-bg',
|
|
370
|
+
'highlight-bg',
|
|
371
|
+
'code-color',
|
|
330
372
|
// Border colors
|
|
331
|
-
'border-color',
|
|
373
|
+
'border-color',
|
|
374
|
+
'border-color-translucent',
|
|
332
375
|
// Focus ring
|
|
333
376
|
'focus-border-color',
|
|
334
377
|
// Form validation
|
|
335
|
-
'form-valid-color',
|
|
336
|
-
'form-
|
|
378
|
+
'form-valid-color',
|
|
379
|
+
'form-valid-border-color',
|
|
380
|
+
'form-invalid-color',
|
|
381
|
+
'form-invalid-border-color',
|
|
337
382
|
]);
|
|
338
383
|
|
|
339
384
|
for (const key of colorTokenKeys) {
|
|
@@ -437,7 +482,10 @@ export interface ValidationOptions {
|
|
|
437
482
|
/**
|
|
438
483
|
* Comprehensive validation of DesignTokens
|
|
439
484
|
*/
|
|
440
|
-
export function validateDesignTokens(
|
|
485
|
+
export function validateDesignTokens(
|
|
486
|
+
tokens: Partial<DesignTokens>,
|
|
487
|
+
options: ValidationOptions = {}
|
|
488
|
+
): ThemeValidationResult {
|
|
441
489
|
const results = [];
|
|
442
490
|
|
|
443
491
|
if (!options.skipRequiredTokens) {
|
|
@@ -461,7 +509,9 @@ export function validateDesignTokens(tokens: Partial<DesignTokens>, options: Val
|
|
|
461
509
|
if (!valid) {
|
|
462
510
|
logger.error(
|
|
463
511
|
'DesignTokens validation failed',
|
|
464
|
-
new Error(
|
|
512
|
+
new Error(
|
|
513
|
+
`Validation failed with ${allErrors.length} errors and ${allWarnings.length} warnings`
|
|
514
|
+
),
|
|
465
515
|
{
|
|
466
516
|
errors: allErrors,
|
|
467
517
|
warnings: allWarnings,
|
|
@@ -469,10 +519,9 @@ export function validateDesignTokens(tokens: Partial<DesignTokens>, options: Val
|
|
|
469
519
|
}
|
|
470
520
|
);
|
|
471
521
|
} else if (allWarnings.length > 0) {
|
|
472
|
-
logger.warn(
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
);
|
|
522
|
+
logger.warn(`DesignTokens validation passed with ${allWarnings.length} warnings`, {
|
|
523
|
+
warnings: allWarnings,
|
|
524
|
+
});
|
|
476
525
|
} else {
|
|
477
526
|
logger.debug('DesignTokens validation passed');
|
|
478
527
|
}
|
|
@@ -498,4 +547,4 @@ export function validateAndMergeTokens(partialTokens?: Partial<DesignTokens>): {
|
|
|
498
547
|
tokens: merged,
|
|
499
548
|
validation,
|
|
500
549
|
};
|
|
501
|
-
}
|
|
550
|
+
}
|