@shohojdhara/atomix 0.2.5 → 0.2.6
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/dist/atomix.css +36 -8
- package/dist/atomix.min.css +1 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.esm.js +81 -39
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +81 -39
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/themes/applemix.css +36 -8
- package/dist/themes/applemix.min.css +1 -1
- package/dist/themes/boomdevs.css +36 -8
- package/dist/themes/boomdevs.min.css +1 -1
- package/dist/themes/esrar.css +36 -8
- package/dist/themes/esrar.min.css +1 -1
- package/dist/themes/flashtrade.css +36 -8
- package/dist/themes/flashtrade.min.css +1 -1
- package/dist/themes/mashroom.css +36 -8
- package/dist/themes/mashroom.min.css +1 -1
- package/dist/themes/shaj-default.css +36 -8
- package/dist/themes/shaj-default.min.css +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +0 -2
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +0 -1
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +121 -11
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +146 -45
- package/src/components/ColorModeToggle/index.ts +1 -1
- package/src/lib/constants/components.ts +1 -1
- package/src/styles/06-components/_components.atomix-glass.scss +4 -3
- package/src/styles/06-components/_components.color-mode-toggle.scss +43 -6
package/package.json
CHANGED
|
@@ -561,7 +561,6 @@ export function AtomixGlass({
|
|
|
561
561
|
width: adjustedSize.width,
|
|
562
562
|
borderRadius: `${effectiveCornerRadius}px`,
|
|
563
563
|
transform: baseStyle.transform,
|
|
564
|
-
transition: baseStyle.transition,
|
|
565
564
|
}}
|
|
566
565
|
/>
|
|
567
566
|
<div
|
|
@@ -580,7 +579,6 @@ export function AtomixGlass({
|
|
|
580
579
|
width: adjustedSize.width,
|
|
581
580
|
borderRadius: `${effectiveCornerRadius}px`,
|
|
582
581
|
transform: baseStyle.transform,
|
|
583
|
-
transition: baseStyle.transition,
|
|
584
582
|
}}
|
|
585
583
|
/>
|
|
586
584
|
{shouldRenderOverLightLayers && (
|
|
@@ -334,7 +334,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
|
|
|
334
334
|
padding: `var(--atomix-glass-container-padding)`,
|
|
335
335
|
borderRadius: `var(--atomix-glass-container-radius)`,
|
|
336
336
|
boxShadow: `var(--atomix-glass-container-box-shadow)`,
|
|
337
|
-
transition: effectiveReducedMotion ? 'none' : 'all 0.2s ease-out',
|
|
338
337
|
}}
|
|
339
338
|
onMouseEnter={onMouseEnter}
|
|
340
339
|
onMouseLeave={onMouseLeave}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import {
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { ColorModeToggle, type ColorMode } from './ColorModeToggle';
|
|
4
|
+
import { Moon, Sun } from '@phosphor-icons/react';
|
|
3
5
|
|
|
4
6
|
const meta = {
|
|
5
7
|
title: 'Components/ColorModeToggle',
|
|
@@ -8,9 +10,26 @@ const meta = {
|
|
|
8
10
|
layout: 'centered',
|
|
9
11
|
},
|
|
10
12
|
argTypes: {
|
|
11
|
-
|
|
12
|
-
control: '
|
|
13
|
-
|
|
13
|
+
size: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['sm', 'md', 'lg'],
|
|
16
|
+
description: 'Size variant',
|
|
17
|
+
},
|
|
18
|
+
disabled: {
|
|
19
|
+
control: 'boolean',
|
|
20
|
+
description: 'Disable the toggle',
|
|
21
|
+
},
|
|
22
|
+
showTooltip: {
|
|
23
|
+
control: 'boolean',
|
|
24
|
+
description: 'Show tooltip on hover',
|
|
25
|
+
},
|
|
26
|
+
disableStorage: {
|
|
27
|
+
control: 'boolean',
|
|
28
|
+
description: 'Disable localStorage persistence',
|
|
29
|
+
},
|
|
30
|
+
disableSystemPreference: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Disable system preference detection',
|
|
14
33
|
},
|
|
15
34
|
},
|
|
16
35
|
} satisfies Meta<typeof ColorModeToggle>;
|
|
@@ -23,22 +42,113 @@ export const Default: Story = {
|
|
|
23
42
|
args: {},
|
|
24
43
|
};
|
|
25
44
|
|
|
26
|
-
//
|
|
27
|
-
export const
|
|
45
|
+
// Size Variants
|
|
46
|
+
export const Sizes: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
|
49
|
+
<ColorModeToggle size="sm" />
|
|
50
|
+
<ColorModeToggle size="md" />
|
|
51
|
+
<ColorModeToggle size="lg" />
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Disabled State
|
|
57
|
+
export const Disabled: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
disabled: true,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Controlled Mode
|
|
64
|
+
export const Controlled: Story = {
|
|
65
|
+
render: () => {
|
|
66
|
+
const [mode, setMode] = useState<ColorMode>('light');
|
|
67
|
+
return (
|
|
68
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', alignItems: 'center' }}>
|
|
69
|
+
<ColorModeToggle value={mode} onChange={setMode} />
|
|
70
|
+
<p>Current mode: {mode}</p>
|
|
71
|
+
<button onClick={() => setMode(mode === 'light' ? 'dark' : 'light')}>
|
|
72
|
+
Toggle from outside
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Custom Icons
|
|
80
|
+
export const CustomIcons: Story = {
|
|
81
|
+
render: () => (
|
|
82
|
+
<ColorModeToggle
|
|
83
|
+
lightIcon={<Moon size={24} weight="fill" />}
|
|
84
|
+
darkIcon={<Sun size={24} weight="fill" />}
|
|
85
|
+
/>
|
|
86
|
+
),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// With Callback
|
|
90
|
+
export const WithCallback: Story = {
|
|
91
|
+
render: () => {
|
|
92
|
+
const [lastChanged, setLastChanged] = useState<string>('');
|
|
93
|
+
return (
|
|
94
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', alignItems: 'center' }}>
|
|
95
|
+
<ColorModeToggle
|
|
96
|
+
onChange={(mode) => setLastChanged(`Changed to ${mode} at ${new Date().toLocaleTimeString()}`)}
|
|
97
|
+
/>
|
|
98
|
+
{lastChanged && <p style={{ fontSize: '0.875rem' }}>{lastChanged}</p>}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Custom Storage Key
|
|
105
|
+
export const CustomStorageKey: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
storageKey: 'my-app-theme',
|
|
108
|
+
dataAttribute: 'data-theme',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Without Storage
|
|
113
|
+
export const WithoutStorage: Story = {
|
|
28
114
|
args: {
|
|
29
|
-
|
|
115
|
+
disableStorage: true,
|
|
30
116
|
},
|
|
31
117
|
};
|
|
32
118
|
|
|
33
|
-
// Example Usage
|
|
34
|
-
export const
|
|
119
|
+
// Example Usage in Header
|
|
120
|
+
export const InHeader: Story = {
|
|
35
121
|
render: () => (
|
|
36
122
|
<div
|
|
37
123
|
className="u-p-5 u-shadow u-d-flex u-justify-content-between u-align-items-center"
|
|
38
|
-
style={{ width: '
|
|
124
|
+
style={{ width: '400px', borderRadius: '8px' }}
|
|
39
125
|
>
|
|
40
|
-
<span>Toggle Theme</span>
|
|
126
|
+
<span style={{ fontWeight: 600 }}>Toggle Theme</span>
|
|
41
127
|
<ColorModeToggle />
|
|
42
128
|
</div>
|
|
43
129
|
),
|
|
44
130
|
};
|
|
131
|
+
|
|
132
|
+
// Multiple Toggles
|
|
133
|
+
export const MultipleToggles: Story = {
|
|
134
|
+
render: () => (
|
|
135
|
+
<div style={{ display: 'flex', gap: '2rem' }}>
|
|
136
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', alignItems: 'center' }}>
|
|
137
|
+
<ColorModeToggle size="sm" />
|
|
138
|
+
<span style={{ fontSize: '0.75rem' }}>Small</span>
|
|
139
|
+
</div>
|
|
140
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', alignItems: 'center' }}>
|
|
141
|
+
<ColorModeToggle size="md" />
|
|
142
|
+
<span style={{ fontSize: '0.75rem' }}>Medium</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', alignItems: 'center' }}>
|
|
145
|
+
<ColorModeToggle size="lg" />
|
|
146
|
+
<span style={{ fontSize: '0.75rem' }}>Large</span>
|
|
147
|
+
</div>
|
|
148
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', alignItems: 'center' }}>
|
|
149
|
+
<ColorModeToggle disabled />
|
|
150
|
+
<span style={{ fontSize: '0.75rem' }}>Disabled</span>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
),
|
|
154
|
+
};
|
|
@@ -1,84 +1,185 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useState, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
export type ColorMode = 'light' | 'dark';
|
|
2
4
|
|
|
3
5
|
export interface ColorModeToggleProps {
|
|
6
|
+
/** Additional CSS class names */
|
|
4
7
|
className?: string;
|
|
8
|
+
/** Inline styles */
|
|
5
9
|
style?: React.CSSProperties;
|
|
10
|
+
/** Controlled mode value */
|
|
11
|
+
value?: ColorMode;
|
|
12
|
+
/** Default mode (uncontrolled) */
|
|
13
|
+
defaultValue?: ColorMode;
|
|
14
|
+
/** Callback when mode changes */
|
|
15
|
+
onChange?: (mode: ColorMode) => void;
|
|
16
|
+
/** Custom light mode icon */
|
|
17
|
+
lightIcon?: React.ReactNode;
|
|
18
|
+
/** Custom dark mode icon */
|
|
19
|
+
darkIcon?: React.ReactNode;
|
|
20
|
+
/** Size variant */
|
|
21
|
+
size?: 'sm' | 'md' | 'lg';
|
|
22
|
+
/** Disable the toggle */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/** localStorage key for persistence */
|
|
25
|
+
storageKey?: string;
|
|
26
|
+
/** data attribute name for body element */
|
|
27
|
+
dataAttribute?: string;
|
|
28
|
+
/** Disable localStorage persistence */
|
|
29
|
+
disableStorage?: boolean;
|
|
30
|
+
/** Disable system preference detection */
|
|
31
|
+
disableSystemPreference?: boolean;
|
|
32
|
+
/** Custom aria-label */
|
|
33
|
+
'aria-label'?: string;
|
|
34
|
+
/** Show tooltip */
|
|
35
|
+
showTooltip?: boolean;
|
|
6
36
|
}
|
|
7
37
|
|
|
8
|
-
|
|
9
|
-
|
|
38
|
+
const DEFAULT_STORAGE_KEY = 'atomix-color-mode';
|
|
39
|
+
const DEFAULT_DATA_ATTRIBUTE = 'data-atomix-color-mode';
|
|
40
|
+
|
|
41
|
+
const SIZE_MAP = {
|
|
42
|
+
sm: 16,
|
|
43
|
+
md: 24,
|
|
44
|
+
lg: 32,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const ColorModeToggle: React.FC<ColorModeToggleProps> = ({
|
|
48
|
+
className = '',
|
|
49
|
+
style,
|
|
50
|
+
value: controlledValue,
|
|
51
|
+
defaultValue = 'light',
|
|
52
|
+
onChange,
|
|
53
|
+
lightIcon,
|
|
54
|
+
darkIcon,
|
|
55
|
+
size = 'md',
|
|
56
|
+
disabled = false,
|
|
57
|
+
storageKey = DEFAULT_STORAGE_KEY,
|
|
58
|
+
dataAttribute = DEFAULT_DATA_ATTRIBUTE,
|
|
59
|
+
disableStorage = false,
|
|
60
|
+
disableSystemPreference = false,
|
|
61
|
+
'aria-label': ariaLabel,
|
|
62
|
+
showTooltip = true,
|
|
63
|
+
}) => {
|
|
64
|
+
const isControlled = controlledValue !== undefined;
|
|
65
|
+
const [internalMode, setInternalMode] = useState<ColorMode>(defaultValue);
|
|
66
|
+
const colorMode = isControlled ? controlledValue : internalMode;
|
|
10
67
|
|
|
11
68
|
// Initialize color mode from localStorage or system preference
|
|
12
69
|
useEffect(() => {
|
|
70
|
+
if (isControlled) return;
|
|
71
|
+
|
|
72
|
+
// SSR check
|
|
73
|
+
if (typeof window === 'undefined') return;
|
|
74
|
+
|
|
13
75
|
// Check if color mode is already set in localStorage
|
|
14
|
-
|
|
76
|
+
if (!disableStorage) {
|
|
77
|
+
try {
|
|
78
|
+
const storedColorMode = localStorage.getItem(storageKey);
|
|
79
|
+
if (storedColorMode === 'light' || storedColorMode === 'dark') {
|
|
80
|
+
setInternalMode(storedColorMode);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.warn('ColorModeToggle: Failed to read from localStorage', error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
15
87
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
88
|
+
// Use system preference if no stored preference
|
|
89
|
+
if (!disableSystemPreference && window.matchMedia) {
|
|
90
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
91
|
+
if (prefersDark) {
|
|
92
|
+
setInternalMode('dark');
|
|
93
|
+
}
|
|
21
94
|
}
|
|
22
|
-
}, []);
|
|
95
|
+
}, [isControlled, disableStorage, disableSystemPreference, storageKey]);
|
|
23
96
|
|
|
24
97
|
// Update the document theme attribute when colorMode changes
|
|
25
98
|
useEffect(() => {
|
|
99
|
+
if (typeof window === 'undefined') return;
|
|
100
|
+
|
|
26
101
|
const validColorMode = colorMode === 'dark' ? 'dark' : 'light';
|
|
27
|
-
document.body.setAttribute(
|
|
28
|
-
|
|
29
|
-
|
|
102
|
+
document.body.setAttribute(dataAttribute, validColorMode);
|
|
103
|
+
|
|
104
|
+
if (!disableStorage) {
|
|
105
|
+
try {
|
|
106
|
+
localStorage.setItem(storageKey, validColorMode);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.warn('ColorModeToggle: Failed to write to localStorage', error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}, [colorMode, dataAttribute, disableStorage, storageKey]);
|
|
30
112
|
|
|
31
113
|
// Listen for system color scheme changes
|
|
32
114
|
useEffect(() => {
|
|
115
|
+
if (isControlled || disableSystemPreference || typeof window === 'undefined') return;
|
|
116
|
+
|
|
33
117
|
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
34
118
|
|
|
35
119
|
const handleSystemThemeChange = (event: MediaQueryListEvent) => {
|
|
36
120
|
// Only update if user hasn't explicitly set a preference
|
|
37
|
-
if (
|
|
38
|
-
|
|
121
|
+
if (disableStorage) {
|
|
122
|
+
setInternalMode(event.matches ? 'dark' : 'light');
|
|
123
|
+
} else {
|
|
124
|
+
try {
|
|
125
|
+
const hasStoredPreference = localStorage.getItem(storageKey);
|
|
126
|
+
if (!hasStoredPreference) {
|
|
127
|
+
setInternalMode(event.matches ? 'dark' : 'light');
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.warn('ColorModeToggle: Failed to check localStorage', error);
|
|
131
|
+
}
|
|
39
132
|
}
|
|
40
133
|
};
|
|
41
134
|
|
|
42
|
-
|
|
43
|
-
if (darkModeMediaQuery.addEventListener) {
|
|
44
|
-
darkModeMediaQuery.addEventListener('change', handleSystemThemeChange);
|
|
45
|
-
} else {
|
|
46
|
-
// Fallback for older browsers
|
|
47
|
-
darkModeMediaQuery.addListener(handleSystemThemeChange);
|
|
48
|
-
}
|
|
135
|
+
darkModeMediaQuery.addEventListener('change', handleSystemThemeChange);
|
|
49
136
|
|
|
50
|
-
// Clean up event listener
|
|
51
137
|
return () => {
|
|
52
|
-
|
|
53
|
-
darkModeMediaQuery.removeEventListener('change', handleSystemThemeChange);
|
|
54
|
-
} else {
|
|
55
|
-
// Fallback for older browsers
|
|
56
|
-
darkModeMediaQuery.removeListener(handleSystemThemeChange);
|
|
57
|
-
}
|
|
138
|
+
darkModeMediaQuery.removeEventListener('change', handleSystemThemeChange);
|
|
58
139
|
};
|
|
59
|
-
}, []);
|
|
140
|
+
}, [isControlled, disableSystemPreference, disableStorage, storageKey]);
|
|
60
141
|
|
|
61
|
-
const toggleColorMode = () => {
|
|
62
|
-
|
|
63
|
-
|
|
142
|
+
const toggleColorMode = useCallback(() => {
|
|
143
|
+
if (disabled) return;
|
|
144
|
+
|
|
145
|
+
const newMode: ColorMode = colorMode === 'light' ? 'dark' : 'light';
|
|
146
|
+
|
|
147
|
+
if (!isControlled) {
|
|
148
|
+
setInternalMode(newMode);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
onChange?.(newMode);
|
|
152
|
+
}, [disabled, colorMode, isControlled, onChange]);
|
|
153
|
+
|
|
154
|
+
const iconSize = SIZE_MAP[size];
|
|
155
|
+
const nextMode = colorMode === 'light' ? 'dark' : 'light';
|
|
156
|
+
const label = ariaLabel || `Switch to ${nextMode} mode`;
|
|
157
|
+
const title = showTooltip ? `Switch to ${nextMode} mode` : undefined;
|
|
158
|
+
|
|
159
|
+
const defaultLightIcon = (
|
|
160
|
+
<svg viewBox="0 0 24 24" width={iconSize} height={iconSize} fill="currentColor" aria-hidden="true">
|
|
161
|
+
<path d="M9.37 5.51c-.18.64-.27 1.31-.27 1.99 0 4.08 3.32 7.4 7.4 7.4.68 0 1.35-.09 1.99-.27C17.45 17.19 14.93 19 12 19c-3.86 0-7-3.14-7-7 0-2.93 1.81-5.45 4.37-6.49zM12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z" />
|
|
162
|
+
</svg>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const defaultDarkIcon = (
|
|
166
|
+
<svg viewBox="0 0 24 24" width={iconSize} height={iconSize} fill="currentColor" aria-hidden="true">
|
|
167
|
+
<path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41.39.39 1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41.39.39 1.03.39 1.41 0l1.06-1.06z" />
|
|
168
|
+
</svg>
|
|
169
|
+
);
|
|
64
170
|
|
|
65
171
|
return (
|
|
66
172
|
<button
|
|
67
|
-
|
|
173
|
+
type="button"
|
|
174
|
+
className={`c-color-mode-toggle c-color-mode-toggle--${size} ${disabled ? 'c-color-mode-toggle--disabled' : ''} ${className}`}
|
|
68
175
|
onClick={toggleColorMode}
|
|
69
|
-
|
|
70
|
-
|
|
176
|
+
disabled={disabled}
|
|
177
|
+
aria-label={label}
|
|
178
|
+
aria-pressed={colorMode === 'dark'}
|
|
179
|
+
title={title}
|
|
71
180
|
style={style}
|
|
72
181
|
>
|
|
73
|
-
{colorMode === 'light' ? (
|
|
74
|
-
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
|
75
|
-
<path d="M9.37 5.51c-.18.64-.27 1.31-.27 1.99 0 4.08 3.32 7.4 7.4 7.4.68 0 1.35-.09 1.99-.27C17.45 17.19 14.93 19 12 19c-3.86 0-7-3.14-7-7 0-2.93 1.81-5.45 4.37-6.49zM12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z" />
|
|
76
|
-
</svg>
|
|
77
|
-
) : (
|
|
78
|
-
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
|
79
|
-
<path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41.39.39 1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41.39.39 1.03.39 1.41 0l1.06-1.06z" />
|
|
80
|
-
</svg>
|
|
81
|
-
)}
|
|
182
|
+
{colorMode === 'light' ? (lightIcon || defaultLightIcon) : (darkIcon || defaultDarkIcon)}
|
|
82
183
|
</button>
|
|
83
184
|
);
|
|
84
185
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as ColorModeToggle } from './ColorModeToggle';
|
|
2
|
-
export type { ColorModeToggleProps } from './ColorModeToggle';
|
|
2
|
+
export type { ColorModeToggleProps, ColorMode } from './ColorModeToggle';
|
|
@@ -1604,7 +1604,7 @@ export const ATOMIX_GLASS = {
|
|
|
1604
1604
|
CORNER_RADIUS: 16, // Default border-radius matching design system
|
|
1605
1605
|
PADDING: '0 0',
|
|
1606
1606
|
MODE: 'standard' as const,
|
|
1607
|
-
OVER_LIGHT:
|
|
1607
|
+
OVER_LIGHT: false as const,
|
|
1608
1608
|
ENABLE_OVER_LIGHT_LAYERS: true,
|
|
1609
1609
|
},
|
|
1610
1610
|
CONSTANTS: {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// CSS custom property defaults
|
|
8
8
|
--atomix-glass-radius: var(--atomix-radius-md, 16px);
|
|
9
9
|
--atomix-glass-transform: none;
|
|
10
|
-
--atomix-glass-transition:
|
|
10
|
+
--atomix-glass-transition: all var(--atomix-transition-duration, 0s) ease-out;
|
|
11
11
|
--atomix-glass-position: absolute;
|
|
12
12
|
--atomix-glass-container-width: 100%;
|
|
13
13
|
--atomix-glass-container-height: 100%;
|
|
@@ -138,14 +138,13 @@
|
|
|
138
138
|
height: var(--atomix-glass-container-height);
|
|
139
139
|
position: relative;
|
|
140
140
|
border-radius: var(--atomix-glass-radius);
|
|
141
|
-
transition:
|
|
141
|
+
transition: var(--atomix-glass-transition);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
&__inner {
|
|
145
145
|
width: var(--atomix-glass-container-width);
|
|
146
146
|
height: var(--atomix-glass-container-height);
|
|
147
147
|
position: relative;
|
|
148
|
-
// Padding and border-radius are set dynamically via inline styles
|
|
149
148
|
border-radius: var(--atomix-glass-radius);
|
|
150
149
|
}
|
|
151
150
|
|
|
@@ -202,10 +201,12 @@
|
|
|
202
201
|
// When both --dark and --over-light modifiers are present
|
|
203
202
|
&--dark#{&}--over-light {
|
|
204
203
|
opacity: var(--atomix-opacity-50, 0.5);
|
|
204
|
+
z-index: -1;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
&--black#{&}--over-light {
|
|
208
208
|
opacity: var(--atomix-opacity-25, 0.25);
|
|
209
|
+
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
// Hidden state modifier
|
|
@@ -12,26 +12,63 @@
|
|
|
12
12
|
cursor: pointer;
|
|
13
13
|
padding: rem(8px);
|
|
14
14
|
border-radius: 50%;
|
|
15
|
-
transition: background-color 0.2s ease;
|
|
15
|
+
transition: background-color 0.2s ease, opacity 0.2s ease;
|
|
16
16
|
color: var(--#{$prefix}body-color);
|
|
17
17
|
|
|
18
|
-
&:hover {
|
|
18
|
+
&:hover:not(:disabled) {
|
|
19
19
|
@include dynamic-background(rgba(0, 0, 0, 0.05));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
&:focus {
|
|
22
|
+
&:focus-visible {
|
|
23
23
|
outline: none;
|
|
24
24
|
box-shadow: 0 0 0 2px var(--#{$prefix}primary);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
&:active:not(:disabled) {
|
|
28
|
+
transform: scale(0.95);
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
svg {
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
display: block;
|
|
33
|
+
transition: transform 0.2s ease;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Size variants
|
|
37
|
+
&--sm {
|
|
38
|
+
padding: rem(6px);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&--md {
|
|
42
|
+
padding: rem(8px);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&--lg {
|
|
46
|
+
padding: rem(10px);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Disabled state
|
|
50
|
+
&--disabled,
|
|
51
|
+
&:disabled {
|
|
52
|
+
cursor: not-allowed;
|
|
53
|
+
opacity: 0.5;
|
|
30
54
|
}
|
|
31
55
|
|
|
32
56
|
[data-#{$prefix}theme='dark'] & {
|
|
33
|
-
&:hover {
|
|
57
|
+
&:hover:not(:disabled) {
|
|
34
58
|
@include dynamic-background(rgba(255, 255, 255, 0.1));
|
|
35
59
|
}
|
|
36
60
|
}
|
|
61
|
+
|
|
62
|
+
// Reduced motion support
|
|
63
|
+
@media (prefers-reduced-motion: reduce) {
|
|
64
|
+
transition: none;
|
|
65
|
+
|
|
66
|
+
svg {
|
|
67
|
+
transition: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&:active:not(:disabled) {
|
|
71
|
+
transform: none;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
37
74
|
}
|