@shohojdhara/atomix 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +3 -3
  2. package/dist/atomix.css +77 -0
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +77 -0
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.js +50 -142
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +2 -2
  9. package/dist/core.js +179 -274
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js +50 -142
  12. package/dist/forms.js.map +1 -1
  13. package/dist/heavy.js +179 -274
  14. package/dist/heavy.js.map +1 -1
  15. package/dist/index.d.ts +1255 -1226
  16. package/dist/index.esm.js +2806 -2958
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +3113 -3269
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.min.js +1 -1
  21. package/dist/index.min.js.map +1 -1
  22. package/dist/theme.d.ts +313 -667
  23. package/dist/theme.js +1818 -2589
  24. package/dist/theme.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
  27. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  28. package/src/components/Button/Button.tsx +85 -167
  29. package/src/components/DataTable/DataTable.stories.tsx +238 -0
  30. package/src/components/DataTable/DataTable.test.tsx +450 -0
  31. package/src/components/DataTable/DataTable.tsx +384 -61
  32. package/src/components/DatePicker/DatePicker.tsx +29 -38
  33. package/src/components/Upload/Upload.tsx +539 -40
  34. package/src/lib/composables/useAtomixGlass.ts +7 -7
  35. package/src/lib/composables/useDataTable.ts +355 -15
  36. package/src/lib/composables/useDatePicker.ts +19 -0
  37. package/src/lib/config/loader.ts +2 -3
  38. package/src/lib/constants/components.ts +17 -0
  39. package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
  40. package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
  41. package/src/lib/theme/adapters/index.ts +1 -4
  42. package/src/lib/theme/config/configLoader.ts +82 -223
  43. package/src/lib/theme/config/loader.ts +15 -21
  44. package/src/lib/theme/constants/constants.ts +1 -1
  45. package/src/lib/theme/core/ThemeRegistry.ts +75 -279
  46. package/src/lib/theme/core/composeTheme.ts +30 -88
  47. package/src/lib/theme/core/createTheme.ts +88 -51
  48. package/src/lib/theme/core/createThemeObject.ts +2 -2
  49. package/src/lib/theme/core/index.ts +15 -2
  50. package/src/lib/theme/errors/errors.ts +1 -1
  51. package/src/lib/theme/generators/generateCSSNested.ts +131 -0
  52. package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
  53. package/src/lib/theme/generators/index.ts +6 -0
  54. package/src/lib/theme/index.ts +45 -27
  55. package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
  56. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
  57. package/src/lib/theme/runtime/ThemeProvider.tsx +393 -544
  58. package/src/lib/theme/runtime/index.ts +1 -0
  59. package/src/lib/theme/runtime/useTheme.ts +1 -1
  60. package/src/lib/theme/runtime/useThemeTokens.ts +122 -0
  61. package/src/lib/theme/test/testTheme.ts +2 -1
  62. package/src/lib/theme/types.ts +14 -14
  63. package/src/lib/theme/utils/componentTheming.ts +140 -0
  64. package/src/lib/theme/utils/domUtils.ts +57 -15
  65. package/src/lib/theme/utils/injectCSS.ts +0 -1
  66. package/src/lib/theme/utils/naming.ts +100 -0
  67. package/src/lib/theme/utils/themeHelpers.ts +1 -39
  68. package/src/lib/theme/utils/themeUtils.ts +1 -170
  69. package/src/lib/types/components.ts +145 -0
  70. package/src/lib/utils/componentUtils.ts +1 -1
  71. package/src/lib/utils/dataTableExport.ts +143 -0
  72. package/src/lib/utils/memoryMonitor.ts +3 -3
  73. package/src/lib/utils/themeNaming.ts +135 -0
  74. package/src/styles/06-components/_components.data-table.scss +95 -0
@@ -7,6 +7,7 @@
7
7
  export { ThemeProvider } from './ThemeProvider';
8
8
  export { ThemeErrorBoundary } from './ThemeErrorBoundary';
9
9
  export { useTheme } from './useTheme';
10
+ export { useThemeTokens } from './useThemeTokens';
10
11
  export { ThemeApplicator, getThemeApplicator, applyTheme } from './ThemeApplicator';
11
12
  export type { ThemeErrorBoundaryProps } from './ThemeErrorBoundary';
12
13
 
@@ -38,7 +38,7 @@ export function useTheme(): UseThemeReturn {
38
38
 
39
39
  return {
40
40
  theme: context.theme,
41
- activeTheme: context.activeTheme,
41
+ activeTokens: context.activeTokens,
42
42
  setTheme: context.setTheme,
43
43
  availableThemes: context.availableThemes,
44
44
  isLoading: context.isLoading,
@@ -0,0 +1,122 @@
1
+ import { useCallback } from 'react';
2
+ import { useTheme } from './useTheme';
3
+ import type { DesignTokens } from '../tokens/tokens';
4
+
5
+ /**
6
+ * Standardized hook for accessing theme tokens in components
7
+ *
8
+ * Provides consistent access to theme values using CSS custom properties
9
+ * and DesignTokens.
10
+ */
11
+ type ThemeTokens = {
12
+ theme: string;
13
+ activeTokens: DesignTokens | null;
14
+ getToken: (tokenName: string, fallback?: string) => string;
15
+ colors: {
16
+ primary: string;
17
+ secondary: string;
18
+ error: string;
19
+ success: string;
20
+ warning: string;
21
+ info: string;
22
+ light: string;
23
+ dark: string;
24
+ };
25
+ spacing: Record<string, string>;
26
+ borderRadius: Record<string, string>;
27
+ typography: {
28
+ fontFamily: Record<string, string>;
29
+ fontSize: Record<string, string>;
30
+ fontWeight: Record<string, string>;
31
+ };
32
+ shadows: Record<string, string>;
33
+ transitions: Record<string, string>;
34
+ };
35
+
36
+ export function useThemeTokens(): ThemeTokens {
37
+ const { theme, activeTokens } = useTheme();
38
+
39
+ // Helper function to get CSS variable value
40
+ const getToken = useCallback((tokenName: string, fallback?: string) => {
41
+ if (typeof window === 'undefined') return fallback || '';
42
+
43
+ const cssVarName = `--atomix-${tokenName}`;
44
+ const computedStyle = getComputedStyle(document.documentElement);
45
+ return computedStyle.getPropertyValue(cssVarName).trim() || fallback || '';
46
+ }, []);
47
+
48
+ // Return unified API for accessing theme values
49
+ // Note: For SSR or direct token access, use activeTokens directly
50
+ return <ThemeTokens>{
51
+ theme,
52
+ activeTokens,
53
+ getToken,
54
+ // Commonly used tokens with fallbacks
55
+ colors: {
56
+ primary: getToken('primary', '#3b82f6'),
57
+ secondary: getToken('secondary', '#10b981'),
58
+ error: getToken('error', '#ef4444'),
59
+ success: getToken('success', '#22c55e'),
60
+ warning: getToken('warning', '#eab308'),
61
+ info: getToken('info', '#3b82f6'),
62
+ light: getToken('light', '#f9fafb'),
63
+ dark: getToken('dark', '#111827'),
64
+ },
65
+ spacing: {
66
+ 1: getToken('spacing-1', '0.25rem'),
67
+ 2: getToken('spacing-2', '0.5rem'),
68
+ 3: getToken('spacing-3', '0.75rem'),
69
+ 4: getToken('spacing-4', '1rem'),
70
+ 5: getToken('spacing-5', '1.25rem'),
71
+ 6: getToken('spacing-6', '1.5rem'),
72
+ 8: getToken('spacing-8', '2rem'),
73
+ 10: getToken('spacing-10', '2.5rem'),
74
+ 12: getToken('spacing-12', '3rem'),
75
+ 16: getToken('spacing-16', '4rem'),
76
+ 20: getToken('spacing-20', '5rem'),
77
+ },
78
+ borderRadius: {
79
+ sm: getToken('border-radius-sm', '0.25rem'),
80
+ md: getToken('border-radius-md', '0.5rem'),
81
+ lg: getToken('border-radius-lg', '0.75rem'),
82
+ xl: getToken('border-radius-xl', '1rem'),
83
+ full: getToken('border-radius-full', '9999px'),
84
+ },
85
+ typography: {
86
+ fontFamily: {
87
+ sans: getToken('font-sans-serif', 'Inter, system-ui, sans-serif'),
88
+ serif: getToken('font-serif', 'Georgia, serif'),
89
+ mono: getToken('font-monospace', 'Fira Code, monospace'),
90
+ },
91
+ fontSize: {
92
+ xs: getToken('font-size-xs', '0.75rem'),
93
+ sm: getToken('font-size-sm', '0.875rem'),
94
+ md: getToken('font-size-md', '1rem'),
95
+ lg: getToken('font-size-lg', '1.125rem'),
96
+ xl: getToken('font-size-xl', '1.25rem'),
97
+ '2xl': getToken('font-size-2xl', '1.5rem'),
98
+ '3xl': getToken('font-size-3xl', '1.875rem'),
99
+ '4xl': getToken('font-size-4xl', '2.25rem'),
100
+ },
101
+ fontWeight: {
102
+ light: getToken('font-weight-light', '300'),
103
+ normal: getToken('font-weight-normal', '400'),
104
+ medium: getToken('font-weight-medium', '500'),
105
+ semibold: getToken('font-weight-semibold', '600'),
106
+ bold: getToken('font-weight-bold', '700'),
107
+ },
108
+ },
109
+ shadows: {
110
+ sm: getToken('box-shadow-sm', '0 1px 2px 0 rgba(0, 0, 0, 0.05)'),
111
+ md: getToken('box-shadow-md', '0 4px 6px -1px rgba(0, 0, 0, 0.1)'),
112
+ lg: getToken('box-shadow-lg', '0 10px 15px -3px rgba(0, 0, 0, 0.1)'),
113
+ xl: getToken('box-shadow-xl', '0 20px 25px -5px rgba(0, 0, 0, 0.1)'),
114
+ inset: getToken('box-shadow-inset', 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)'),
115
+ },
116
+ transitions: {
117
+ fast: getToken('transition-fast', '150ms'),
118
+ base: getToken('transition-base', '200ms'),
119
+ slow: getToken('transition-slow', '300ms'),
120
+ },
121
+ };
122
+ }
@@ -270,7 +270,8 @@ export function createTestThemeObject(): Theme {
270
270
  */
271
271
  export function generateTestThemeCSSFromObject(): string {
272
272
  const theme = createTestThemeObject();
273
- return createTheme(theme); // createTheme accepts both formats
273
+ const tokens = themeToDesignTokens(theme);
274
+ return createTheme(tokens);
274
275
  }
275
276
 
276
277
  // ============================================================================
@@ -50,8 +50,8 @@ export interface ThemeChangeEvent {
50
50
  previousTheme: string | null;
51
51
  /** New theme name */
52
52
  currentTheme: string;
53
- /** Theme object (for JS themes) */
54
- themeObject?: Theme | null;
53
+ /** DesignTokens if using tokens-based theme */
54
+ tokens?: import('./tokens').DesignTokens | null;
55
55
  /** Timestamp of the change */
56
56
  timestamp: number;
57
57
  /** Whether the change was from user action or system */
@@ -126,7 +126,7 @@ export interface UseThemeOptions {
126
126
  /** Custom storage key */
127
127
  storageKey?: string;
128
128
  /** Callback when theme changes */
129
- onChange?: (theme: string | Theme | import('./tokens').DesignTokens) => void;
129
+ onChange?: (theme: string | import('./tokens').DesignTokens) => void;
130
130
  }
131
131
 
132
132
  /**
@@ -135,10 +135,10 @@ export interface UseThemeOptions {
135
135
  export interface UseThemeReturn {
136
136
  /** Current theme name */
137
137
  theme: string;
138
- /** Current active theme object (for JS themes) */
139
- activeTheme: Theme | null;
140
- /** Function to change theme (supports string, Theme, or DesignTokens) */
141
- setTheme: (theme: string | Theme | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>, options?: ThemeLoadOptions) => Promise<void>;
138
+ /** Current active DesignTokens (if using tokens-based theme) */
139
+ activeTokens: import('./tokens').DesignTokens | null;
140
+ /** Function to change theme (supports string or DesignTokens) */
141
+ setTheme: (theme: string | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>, options?: ThemeLoadOptions) => Promise<void>;
142
142
  /** Available themes */
143
143
  availableThemes: ThemeMetadata[];
144
144
  /** Whether a theme is currently loading */
@@ -213,8 +213,8 @@ export interface ThemeComponentOverrides {
213
213
  export interface ThemeProviderProps {
214
214
  /** Child components */
215
215
  children: React.ReactNode;
216
- /** Default theme */
217
- defaultTheme?: string | Theme;
216
+ /** Default theme (string name or DesignTokens) */
217
+ defaultTheme?: string | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>;
218
218
  /** Available themes */
219
219
  themes?: Record<string, ThemeMetadata>;
220
220
  /** Base path for theme CSS */
@@ -234,7 +234,7 @@ export interface ThemeProviderProps {
234
234
  /** Use minified CSS */
235
235
  useMinified?: boolean;
236
236
  /** Callback when theme changes */
237
- onThemeChange?: (theme: string | Theme | import('./tokens').DesignTokens) => void;
237
+ onThemeChange?: (theme: string | import('./tokens').DesignTokens) => void;
238
238
  /** Callback on error */
239
239
  onError?: (error: Error, themeName: string) => void;
240
240
  }
@@ -245,10 +245,10 @@ export interface ThemeProviderProps {
245
245
  export interface ThemeContextValue {
246
246
  /** Current theme name */
247
247
  theme: string;
248
- /** Current active theme object (for JS themes) */
249
- activeTheme: Theme | null;
250
- /** Set theme function (supports string, Theme, or DesignTokens) */
251
- setTheme: (theme: string | Theme | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>, options?: ThemeLoadOptions) => Promise<void>;
248
+ /** Current active DesignTokens (if using tokens-based theme) */
249
+ activeTokens: import('./tokens').DesignTokens | null;
250
+ /** Set theme function (supports string or DesignTokens) */
251
+ setTheme: (theme: string | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>, options?: ThemeLoadOptions) => Promise<void>;
252
252
  /** Available themes */
253
253
  availableThemes: ThemeMetadata[];
254
254
  /** Loading state */
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Component Theming Utilities
3
+ *
4
+ * Provides consistent patterns for applying theme values to components
5
+ * using DesignTokens and CSS variables
6
+ */
7
+
8
+ import type { DesignTokens } from '../tokens/tokens';
9
+
10
+ export interface ComponentThemeOptions {
11
+ component: string;
12
+ variant?: string;
13
+ size?: string;
14
+ tokens?: Partial<DesignTokens>;
15
+ }
16
+
17
+ /**
18
+ * Get a theme value for a specific component using CSS variables
19
+ * This ensures all components access theme values consistently
20
+ */
21
+ export function getComponentThemeValue(
22
+ component: string,
23
+ property: string,
24
+ variant?: string,
25
+ size?: string
26
+ ): string {
27
+ // Build CSS variable name following consistent pattern
28
+ const parts = ['atomix', component];
29
+
30
+ if (variant) {
31
+ parts.push(variant);
32
+ }
33
+
34
+ if (size) {
35
+ parts.push(size);
36
+ }
37
+
38
+ parts.push(property);
39
+
40
+ const cssVarName = `--${parts.join('-')}`;
41
+ const fallbackVarName = `--atomix-${property}`;
42
+
43
+ // Return CSS variable reference with fallback
44
+ return `var(${cssVarName}, var(${fallbackVarName}, initial))`;
45
+ }
46
+
47
+ /**
48
+ * Generate component-specific CSS variables from DesignTokens
49
+ */
50
+ export function generateComponentCSSVars(
51
+ component: string,
52
+ tokens?: Partial<DesignTokens>,
53
+ variant?: string,
54
+ size?: string
55
+ ): Record<string, string> {
56
+ const vars: Record<string, string> = {};
57
+
58
+ if (!tokens) return vars;
59
+
60
+ const prefixParts = ['atomix', component];
61
+
62
+ if (variant) {
63
+ prefixParts.push(variant);
64
+ }
65
+
66
+ if (size) {
67
+ prefixParts.push(size);
68
+ }
69
+
70
+ const prefix = prefixParts.join('-');
71
+
72
+ // Map common DesignTokens to component-specific CSS variables
73
+ if (tokens.primary) {
74
+ vars[`--${prefix}-color`] = tokens.primary;
75
+ }
76
+ if (tokens['primary-9']) {
77
+ vars[`--${prefix}-color-hover`] = tokens['primary-9'];
78
+ }
79
+ if (tokens['body-color']) {
80
+ vars[`--${prefix}-color-disabled`] = tokens['body-color'];
81
+ }
82
+
83
+ if (tokens['body-font-family']) {
84
+ vars[`--${prefix}-font-family`] = tokens['body-font-family'];
85
+ }
86
+ if (tokens['body-font-size']) {
87
+ vars[`--${prefix}-font-size`] = tokens['body-font-size'];
88
+ }
89
+
90
+ if (tokens['spacing-1']) {
91
+ vars[`--${prefix}-spacing-sm`] = tokens['spacing-1'];
92
+ }
93
+ if (tokens['spacing-2']) {
94
+ vars[`--${prefix}-spacing-md`] = tokens['spacing-2'];
95
+ }
96
+ if (tokens['spacing-4']) {
97
+ vars[`--${prefix}-spacing-lg`] = tokens['spacing-4'];
98
+ }
99
+
100
+ return vars;
101
+ }
102
+
103
+ /**
104
+ * Apply consistent theme to component style object using DesignTokens
105
+ */
106
+ export function applyComponentTheme(
107
+ component: string,
108
+ style: React.CSSProperties = {},
109
+ variant?: string,
110
+ size?: string,
111
+ tokens?: Partial<DesignTokens>
112
+ ): React.CSSProperties {
113
+ // If no tokens provided, return original style
114
+ if (!tokens) {
115
+ return style;
116
+ }
117
+
118
+ // Generate component-specific CSS variables
119
+ const componentVars = generateComponentCSSVars(component, tokens, variant, size);
120
+
121
+ // Merge with existing style
122
+ return {
123
+ ...componentVars,
124
+ ...style,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Create a hook for consistent component theming
130
+ */
131
+ export function useComponentTheme(
132
+ component: string,
133
+ variant?: string,
134
+ size?: string,
135
+ tokens?: Partial<DesignTokens>
136
+ ): (property: string) => string {
137
+ return (property: string) => {
138
+ return getComponentThemeValue(component, property, variant, size);
139
+ };
140
+ }
@@ -7,6 +7,7 @@
7
7
 
8
8
  import type { ThemeMetadata, ThemeValidationResult } from '../types';
9
9
  import { THEME_LINK_ID_PREFIX } from '../constants/constants';
10
+ import { ThemeError, ThemeErrorCode } from '../errors/errors';
10
11
 
11
12
  /**
12
13
  * Check if code is running in a browser environment
@@ -29,6 +30,20 @@ export const getThemeLinkId = (themeName: string): string => {
29
30
  return `${THEME_LINK_ID_PREFIX}${themeName}`;
30
31
  };
31
32
 
33
+ /**
34
+ * Sanitize path to prevent path injection attacks
35
+ *
36
+ * @param path - Path to sanitize
37
+ * @returns Sanitized path
38
+ */
39
+ const sanitizePath = (path: string): string => {
40
+ return path
41
+ .replace(/[<>"']/g, '') // Remove dangerous characters
42
+ .replace(/\.\./g, '') // Remove path traversal attempts
43
+ .replace(/\/+/g, '/') // Normalize multiple slashes
44
+ .replace(/^\/+|\/+$/g, ''); // Trim leading/trailing slashes
45
+ };
46
+
32
47
  /**
33
48
  * Build the CSS file path for a theme
34
49
  *
@@ -37,6 +52,7 @@ export const getThemeLinkId = (themeName: string): string => {
37
52
  * @param useMinified - Whether to use minified CSS
38
53
  * @param cdnPath - Optional CDN path
39
54
  * @returns Full path to the theme CSS file
55
+ * @throws Error if theme name is invalid
40
56
  */
41
57
  export const buildThemePath = (
42
58
  themeName: string,
@@ -46,21 +62,24 @@ export const buildThemePath = (
46
62
  ): string => {
47
63
  // Validate theme name to prevent path injection
48
64
  if (!isValidThemeName(themeName)) {
49
- throw new Error(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens.`);
65
+ throw new ThemeError(
66
+ `Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens (e.g., "my-theme").`,
67
+ ThemeErrorCode.INVALID_THEME_NAME,
68
+ { themeName, pattern: /^[a-z0-9]+(-[a-z0-9]+)*$/ }
69
+ );
50
70
  }
51
71
 
52
72
  const extension = useMinified ? '.min.css' : '.css';
53
73
  const fileName = `${themeName}${extension}`;
54
74
 
55
75
  if (cdnPath) {
56
- // Validate CDN path doesn't contain dangerous characters
57
- const cleanCdnPath = cdnPath.replace(/[<>"']/g, '');
76
+ // Sanitize CDN path to prevent path injection
77
+ const cleanCdnPath = sanitizePath(cdnPath);
58
78
  return `${cleanCdnPath}/${fileName}`;
59
79
  }
60
80
 
61
- // Ensure basePath doesn't end with slash and fileName doesn't start with slash
62
- // Also sanitize basePath to prevent path injection
63
- const cleanBasePath = basePath.replace(/\/$/, '').replace(/[<>"']/g, '');
81
+ // Sanitize basePath to prevent path injection
82
+ const cleanBasePath = sanitizePath(basePath);
64
83
  const cleanFileName = fileName.replace(/^\//, '');
65
84
 
66
85
  return `${cleanBasePath}/${cleanFileName}`;
@@ -110,7 +129,11 @@ export const loadThemeCSS = (
110
129
  link.onerror = () => {
111
130
  // Remove failed link element
112
131
  link.remove();
113
- reject(new Error(`Failed to load theme CSS: ${fullPath}`));
132
+ reject(new ThemeError(
133
+ `Failed to load theme CSS from: ${fullPath}. Please check that the file exists and is accessible.`,
134
+ ThemeErrorCode.THEME_LOAD_FAILED,
135
+ { fullPath, linkId }
136
+ ));
114
137
  };
115
138
 
116
139
  // Append to head
@@ -166,8 +189,10 @@ export const applyThemeAttributes = (
166
189
  return;
167
190
  }
168
191
 
169
- // Set data attribute on body
170
- document.body.setAttribute(dataAttribute, themeName);
192
+ // Set data attribute on body (with null check)
193
+ if (document.body) {
194
+ document.body.setAttribute(dataAttribute, themeName);
195
+ }
171
196
 
172
197
  // Also set on documentElement for broader compatibility
173
198
  document.documentElement.setAttribute(dataAttribute, themeName);
@@ -185,7 +210,12 @@ export const removeThemeAttributes = (
185
210
  return;
186
211
  }
187
212
 
188
- document.body.removeAttribute(dataAttribute);
213
+ // Remove from body (with null check)
214
+ if (document.body) {
215
+ document.body.removeAttribute(dataAttribute);
216
+ }
217
+
218
+ // Remove from documentElement
189
219
  document.documentElement.removeAttribute(dataAttribute);
190
220
  };
191
221
 
@@ -202,8 +232,10 @@ export const getCurrentThemeFromDOM = (
202
232
  return null;
203
233
  }
204
234
 
205
- return document.body.getAttribute(dataAttribute) ||
206
- document.documentElement.getAttribute(dataAttribute);
235
+ // Add null checks for SSR safety
236
+ const bodyTheme = document.body?.getAttribute(dataAttribute);
237
+ const htmlTheme = document.documentElement?.getAttribute(dataAttribute);
238
+ return bodyTheme || htmlTheme || null;
207
239
  };
208
240
 
209
241
  /**
@@ -377,15 +409,15 @@ export const createLocalStorageAdapter = () => {
377
409
  *
378
410
  * @param func - Function to debounce
379
411
  * @param wait - Wait time in milliseconds
380
- * @returns Debounced function
412
+ * @returns Debounced function with cancel method
381
413
  */
382
414
  export const debounce = <T extends (...args: any[]) => any>(
383
415
  func: T,
384
416
  wait: number
385
- ): ((...args: Parameters<T>) => void) => {
417
+ ): ((...args: Parameters<T>) => void) & { cancel: () => void } => {
386
418
  let timeout: ReturnType<typeof setTimeout> | null = null;
387
419
 
388
- return function executedFunction(...args: Parameters<T>) {
420
+ const debounced = function executedFunction(...args: Parameters<T>) {
389
421
  const later = () => {
390
422
  timeout = null;
391
423
  func(...args);
@@ -396,4 +428,14 @@ export const debounce = <T extends (...args: any[]) => any>(
396
428
  }
397
429
  timeout = setTimeout(later, wait);
398
430
  };
431
+
432
+ // Add cancel method for cleanup
433
+ debounced.cancel = () => {
434
+ if (timeout !== null) {
435
+ clearTimeout(timeout);
436
+ timeout = null;
437
+ }
438
+ };
439
+
440
+ return debounced;
399
441
  };
@@ -34,7 +34,6 @@ export function injectCSS(
34
34
  id: string = 'atomix-theme'
35
35
  ): void {
36
36
  if (!isBrowser()) {
37
- console.warn('injectCSS: Not in browser environment, CSS not injected');
38
37
  return;
39
38
  }
40
39
 
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Naming Utilities
3
+ *
4
+ * Provides consistent naming conventions across the theme system
5
+ */
6
+
7
+ export interface NamingOptions {
8
+ prefix?: string;
9
+ component?: string;
10
+ variant?: string;
11
+ state?: string;
12
+ }
13
+
14
+ /**
15
+ * Generate consistent CSS class names following BEM methodology
16
+ */
17
+ export function generateClassName(block: string, element?: string, modifiers?: Record<string, boolean | string>): string {
18
+ let className = block;
19
+
20
+ if (element) {
21
+ className += `__${element}`;
22
+ }
23
+
24
+ if (modifiers) {
25
+ Object.entries(modifiers).forEach(([key, value]) => {
26
+ if (value) {
27
+ className += `--${key}`;
28
+ if (typeof value === 'string' && value !== key) {
29
+ className += `-${value}`;
30
+ }
31
+ }
32
+ });
33
+ }
34
+
35
+ return className;
36
+ }
37
+
38
+ /**
39
+ * Generate consistent CSS variable names
40
+ */
41
+ export function generateCSSVariableName(property: string, options: NamingOptions = {}): string {
42
+ const { prefix = 'atomix', component, variant, state } = options;
43
+
44
+ const parts = [prefix];
45
+
46
+ if (component) {
47
+ parts.push(component);
48
+ }
49
+
50
+ if (variant) {
51
+ parts.push(variant);
52
+ }
53
+
54
+ if (state) {
55
+ parts.push(state);
56
+ }
57
+
58
+ parts.push(property);
59
+
60
+ return `--${parts.join('-')}`;
61
+ }
62
+
63
+ /**
64
+ * Normalize theme tokens to consistent naming convention
65
+ */
66
+ export function normalizeThemeTokens(tokens: Record<string, any>): Record<string, any> {
67
+ const normalized: Record<string, any> = {};
68
+
69
+ for (const [key, value] of Object.entries(tokens)) {
70
+ if (typeof value === 'object' && value !== null) {
71
+ // Recursively normalize nested objects
72
+ normalized[key] = normalizeThemeTokens(value);
73
+ } else {
74
+ // Apply consistent naming transformation
75
+ normalized[key] = value;
76
+ }
77
+ }
78
+
79
+ return normalized;
80
+ }
81
+
82
+ /**
83
+ * Convert camelCase to kebab-case for CSS custom properties
84
+ */
85
+ export function camelToKebab(str: string): string {
86
+ return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
87
+ }
88
+
89
+ /**
90
+ * Convert theme property to CSS variable name
91
+ */
92
+ export function themePropertyToCSSVar(propertyPath: string, prefix: string = 'atomix'): string {
93
+ // Convert nested property paths to kebab-case
94
+ const path = propertyPath
95
+ .split('.')
96
+ .map(part => camelToKebab(part))
97
+ .join('-');
98
+
99
+ return `--${prefix}-${path}`;
100
+ }
@@ -1,34 +1,10 @@
1
1
  /**
2
2
  * Theme Helper Functions
3
3
  *
4
- * Utility functions for working with themes and DesignTokens
4
+ * Utility functions for working with DesignTokens
5
5
  */
6
6
 
7
- import type { Theme } from '../types';
8
7
  import type { DesignTokens } from '../tokens/tokens';
9
- import { createDesignTokensFromTheme } from '../adapters/themeAdapter';
10
-
11
- /**
12
- * Get DesignTokens from current theme
13
- *
14
- * Converts a Theme object to DesignTokens. Useful when you need to
15
- * work with DesignTokens but have a Theme object.
16
- *
17
- * @param theme - Theme object to convert
18
- * @returns DesignTokens object
19
- *
20
- * @example
21
- * ```typescript
22
- * // If you have a Theme object, convert it to DesignTokens
23
- * const tokens = getDesignTokensFromTheme(theme);
24
- * // Now you can use tokens with unified theme system
25
- * const css = createTheme(tokens);
26
- * ```
27
- */
28
- export function getDesignTokensFromTheme(theme: Theme | null): DesignTokens | null {
29
- if (!theme) return null;
30
- return createDesignTokensFromTheme(theme);
31
- }
32
8
 
33
9
  /**
34
10
  * Check if a value is DesignTokens
@@ -62,17 +38,3 @@ export function isDesignTokens(value: unknown): value is DesignTokens {
62
38
  return hasDesignTokenKeys;
63
39
  }
64
40
 
65
- /**
66
- * Check if a value is a Theme object
67
- *
68
- * Type guard to check if an object is a Theme.
69
- *
70
- * @param value - Value to check
71
- * @returns True if value is Theme
72
- */
73
- export function isThemeObject(value: unknown): value is Theme {
74
- if (!value || typeof value !== 'object') return false;
75
- const obj = value as Record<string, unknown>;
76
- return '__isJSTheme' in obj || ('palette' in obj && 'typography' in obj);
77
- }
78
-