@fragments-sdk/ui 0.9.7 → 0.10.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/README.md +32 -24
- package/dist/components/Theme/index.cjs +86 -1
- package/dist/components/Theme/index.cjs.map +1 -1
- package/dist/components/Theme/index.d.ts +44 -1
- package/dist/components/Theme/index.d.ts.map +1 -1
- package/dist/components/Theme/index.js +86 -1
- package/dist/components/Theme/index.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/fragments.json +1 -1
- package/package.json +6 -2
- package/src/components/Theme/index.tsx +168 -1
- package/src/index.ts +6 -0
- package/src/tokens/_seeds.scss +20 -0
package/README.md
CHANGED
|
@@ -16,12 +16,19 @@ npm install react react-dom
|
|
|
16
16
|
|
|
17
17
|
## Setup
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**Quick start (no SCSS)** — import the prebuilt CSS in your app entry point. This loads component styles with default tokens:
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
22
|
import '@fragments-sdk/ui/styles';
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
**Custom theming (SCSS)** — create a `.scss` file with `@use '@fragments-sdk/ui/styles' with (...)` to set your seed values, then import both:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import '@fragments-sdk/ui/styles'; // component styles (ui.css)
|
|
29
|
+
import './styles/globals.scss'; // your seed overrides
|
|
30
|
+
```
|
|
31
|
+
|
|
25
32
|
Then use components:
|
|
26
33
|
|
|
27
34
|
```tsx
|
|
@@ -116,28 +123,27 @@ All fragment and block previews are authored source snippets, not runtime-serial
|
|
|
116
123
|
|
|
117
124
|
### Seed-Based Configuration (Recommended)
|
|
118
125
|
|
|
119
|
-
Configure ~5
|
|
126
|
+
Configure ~5 seeds and everything derives automatically using the SCSS `@use ... with()` syntax:
|
|
120
127
|
|
|
121
128
|
```scss
|
|
122
129
|
// styles/globals.scss
|
|
123
130
|
|
|
124
|
-
// Minimal setup
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
// Minimal setup — just your brand color
|
|
132
|
+
@use '@fragments-sdk/ui/styles' with (
|
|
133
|
+
$fui-brand: #0066ff
|
|
134
|
+
);
|
|
127
135
|
```
|
|
128
136
|
|
|
129
137
|
```scss
|
|
130
138
|
// Full customization
|
|
131
|
-
|
|
132
|
-
$fui-
|
|
133
|
-
$fui-
|
|
134
|
-
$fui-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
$fui-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
@use '@fragments-sdk/ui/globals';
|
|
139
|
+
@use '@fragments-sdk/ui/styles' with (
|
|
140
|
+
$fui-brand: #0066ff,
|
|
141
|
+
$fui-neutral: "ice",
|
|
142
|
+
$fui-density: "compact",
|
|
143
|
+
$fui-radius-style: "rounded",
|
|
144
|
+
$fui-danger: #dc2626,
|
|
145
|
+
$fui-success: #16a34a
|
|
146
|
+
);
|
|
141
147
|
```
|
|
142
148
|
|
|
143
149
|
#### Available Seeds
|
|
@@ -145,7 +151,7 @@ $fui-success: #16a34a;
|
|
|
145
151
|
| Seed | Type | Default | Description |
|
|
146
152
|
|------|------|---------|-------------|
|
|
147
153
|
| `$fui-brand` | Color | `#18181b` | Primary brand color - derives accent, focus rings, etc. |
|
|
148
|
-
| `$fui-neutral` | String | `"
|
|
154
|
+
| `$fui-neutral` | String | `"stone"` | Neutral palette for surfaces, text, borders |
|
|
149
155
|
| `$fui-density` | String | `"default"` | Spacing density scale |
|
|
150
156
|
| `$fui-radius-style` | String | `"default"` | Corner radius style |
|
|
151
157
|
| `$fui-danger` | Color | `#ef4444` | Error/danger semantic color |
|
|
@@ -157,11 +163,11 @@ $fui-success: #16a34a;
|
|
|
157
163
|
|
|
158
164
|
| Name | Description |
|
|
159
165
|
|------|-------------|
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
166
|
+
| `stone` | Cool gray neutrals (balanced, professional) — default |
|
|
167
|
+
| `ice` | Cool blue-tinted grays (crisp, technical) |
|
|
168
|
+
| `earth` | Warm brown-tinted grays (natural, grounded) |
|
|
169
|
+
| `sand` | Warm tan-tinted grays (organic, approachable) |
|
|
170
|
+
| `fire` | Warm red-tinted grays (bold, energetic) |
|
|
165
171
|
|
|
166
172
|
#### Density Presets
|
|
167
173
|
|
|
@@ -198,7 +204,7 @@ You can still override individual tokens directly:
|
|
|
198
204
|
### Breakpoints
|
|
199
205
|
|
|
200
206
|
```scss
|
|
201
|
-
@use '@fragments-sdk/ui/
|
|
207
|
+
@use '@fragments-sdk/ui/mixins' as *;
|
|
202
208
|
|
|
203
209
|
.responsive {
|
|
204
210
|
@include breakpoint-md {
|
|
@@ -238,8 +244,10 @@ You can still override individual tokens directly:
|
|
|
238
244
|
// ...many more
|
|
239
245
|
|
|
240
246
|
// After: just seeds
|
|
241
|
-
|
|
242
|
-
|
|
247
|
+
@use '@fragments-sdk/ui/styles' with (
|
|
248
|
+
$fui-brand: #0066ff,
|
|
249
|
+
$fui-neutral: "ice"
|
|
250
|
+
);
|
|
243
251
|
```
|
|
244
252
|
|
|
245
253
|
2. Dark mode, hover states, and derived colors are computed automatically
|
|
@@ -102,6 +102,11 @@ function ThemeProvider({
|
|
|
102
102
|
attribute = "data-theme"
|
|
103
103
|
}) {
|
|
104
104
|
const systemPreference = useSystemPreference();
|
|
105
|
+
if (process.env.NODE_ENV !== "production" && defaultTheme !== void 0) {
|
|
106
|
+
console.warn(
|
|
107
|
+
"[Fragments] ThemeProvider: `defaultTheme` is deprecated. Use `defaultMode` instead. `defaultTheme` will be removed in v1.0."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
105
110
|
const resolvedDefault = defaultMode ?? defaultTheme ?? "system";
|
|
106
111
|
const [internalMode, setInternalMode] = React__namespace.useState(resolvedDefault);
|
|
107
112
|
const [mounted, setMounted] = React__namespace.useState(false);
|
|
@@ -225,13 +230,93 @@ function ThemeToggle({
|
|
|
225
230
|
}
|
|
226
231
|
);
|
|
227
232
|
}
|
|
228
|
-
Object.assign(ThemeProvider, {
|
|
233
|
+
const Theme = Object.assign(ThemeProvider, {
|
|
229
234
|
Root: ThemeProvider,
|
|
230
235
|
Provider: ThemeProvider,
|
|
231
236
|
Toggle: ThemeToggle,
|
|
232
237
|
useTheme
|
|
233
238
|
});
|
|
239
|
+
const RADIUS_PRESETS = {
|
|
240
|
+
sharp: { sm: "0", md: "0", lg: "0", xl: "0" },
|
|
241
|
+
subtle: { sm: "0.125rem", md: "0.25rem", lg: "0.375rem", xl: "0.5rem" },
|
|
242
|
+
default: { sm: "0.25rem", md: "0.429rem", lg: "0.571rem", xl: "0.857rem" },
|
|
243
|
+
rounded: { sm: "0.375rem", md: "0.5rem", lg: "0.75rem", xl: "1rem" },
|
|
244
|
+
pill: { sm: "0.5rem", md: "0.75rem", lg: "1rem", xl: "1.5rem" }
|
|
245
|
+
};
|
|
246
|
+
const DENSITY_CONFIGS = {
|
|
247
|
+
compact: { baseUnit: 6, baseFontSize: 14, buttonHeights: [24, 30, 36], inputHeights: [24, 32, 36] },
|
|
248
|
+
default: { baseUnit: 7, baseFontSize: 14, buttonHeights: [28, 36, 44], inputHeights: [28, 40, 44] },
|
|
249
|
+
relaxed: { baseUnit: 8, baseFontSize: 14, buttonHeights: [32, 40, 48], inputHeights: [32, 44, 48] }
|
|
250
|
+
};
|
|
251
|
+
function pxToRem(px, baseFontSize) {
|
|
252
|
+
return `${px / baseFontSize}rem`;
|
|
253
|
+
}
|
|
254
|
+
function hexToRgb(hex) {
|
|
255
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
256
|
+
if (!result) return null;
|
|
257
|
+
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
|
258
|
+
}
|
|
259
|
+
function adjustLightness(hex, amount) {
|
|
260
|
+
const rgb = hexToRgb(hex);
|
|
261
|
+
if (!rgb) return hex;
|
|
262
|
+
const [r, g, b] = rgb;
|
|
263
|
+
const adjust = (v) => Math.max(0, Math.min(255, Math.round(v + amount)));
|
|
264
|
+
return `#${adjust(r).toString(16).padStart(2, "0")}${adjust(g).toString(16).padStart(2, "0")}${adjust(b).toString(16).padStart(2, "0")}`;
|
|
265
|
+
}
|
|
266
|
+
function setVar(el, name, value) {
|
|
267
|
+
el.style.setProperty(name, value);
|
|
268
|
+
}
|
|
269
|
+
function configureTheme(options) {
|
|
270
|
+
if (typeof document === "undefined") return;
|
|
271
|
+
const root = document.documentElement;
|
|
272
|
+
if (options.brand) {
|
|
273
|
+
setVar(root, "--fui-color-accent", options.brand);
|
|
274
|
+
setVar(root, "--fui-color-accent-hover", adjustLightness(options.brand, -20));
|
|
275
|
+
setVar(root, "--fui-color-accent-active", adjustLightness(options.brand, -40));
|
|
276
|
+
setVar(root, "--fui-focus-ring-color", `${options.brand}66`);
|
|
277
|
+
}
|
|
278
|
+
if (options.danger) {
|
|
279
|
+
setVar(root, "--fui-color-danger", options.danger);
|
|
280
|
+
setVar(root, "--fui-color-danger-hover", adjustLightness(options.danger, -20));
|
|
281
|
+
}
|
|
282
|
+
if (options.success) setVar(root, "--fui-color-success", options.success);
|
|
283
|
+
if (options.warning) setVar(root, "--fui-color-warning", options.warning);
|
|
284
|
+
if (options.info) setVar(root, "--fui-color-info", options.info);
|
|
285
|
+
if (options.radiusStyle) {
|
|
286
|
+
const r = RADIUS_PRESETS[options.radiusStyle];
|
|
287
|
+
if (r) {
|
|
288
|
+
setVar(root, "--fui-radius-sm", r.sm);
|
|
289
|
+
setVar(root, "--fui-radius-md", r.md);
|
|
290
|
+
setVar(root, "--fui-radius-lg", r.lg);
|
|
291
|
+
setVar(root, "--fui-radius-xl", r.xl);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (options.density) {
|
|
295
|
+
const d = DENSITY_CONFIGS[options.density];
|
|
296
|
+
if (d) {
|
|
297
|
+
const unitRem = d.baseUnit / d.baseFontSize;
|
|
298
|
+
setVar(root, "--fui-space-1", `${unitRem}rem`);
|
|
299
|
+
setVar(root, "--fui-space-2", `${unitRem * 2}rem`);
|
|
300
|
+
setVar(root, "--fui-space-3", `${unitRem * 3}rem`);
|
|
301
|
+
setVar(root, "--fui-space-4", `${unitRem * 4}rem`);
|
|
302
|
+
setVar(root, "--fui-space-5", `${unitRem * 5}rem`);
|
|
303
|
+
setVar(root, "--fui-space-6", `${unitRem * 6}rem`);
|
|
304
|
+
setVar(root, "--fui-space-8", `${unitRem * 8}rem`);
|
|
305
|
+
setVar(root, "--fui-space-10", `${unitRem * 10}rem`);
|
|
306
|
+
setVar(root, "--fui-space-12", `${unitRem * 12}rem`);
|
|
307
|
+
setVar(root, "--fui-button-height-sm", pxToRem(d.buttonHeights[0], d.baseFontSize));
|
|
308
|
+
setVar(root, "--fui-button-height-md", pxToRem(d.buttonHeights[1], d.baseFontSize));
|
|
309
|
+
setVar(root, "--fui-button-height-lg", pxToRem(d.buttonHeights[2], d.baseFontSize));
|
|
310
|
+
setVar(root, "--fui-input-height-sm", pxToRem(d.inputHeights[0], d.baseFontSize));
|
|
311
|
+
setVar(root, "--fui-input-height", pxToRem(d.inputHeights[1], d.baseFontSize));
|
|
312
|
+
setVar(root, "--fui-input-height-lg", pxToRem(d.inputHeights[2], d.baseFontSize));
|
|
313
|
+
setVar(root, "--fui-base-unit", `${d.baseUnit}px`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
exports.Theme = Theme;
|
|
234
318
|
exports.ThemeProvider = ThemeProvider;
|
|
235
319
|
exports.ThemeToggle = ThemeToggle;
|
|
320
|
+
exports.configureTheme = configureTheme;
|
|
236
321
|
exports.useTheme = useTheme;
|
|
237
322
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../src/components/Theme/index.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport styles from './ThemeToggle.module.scss';\n\n// ============================================\n// Types\n// ============================================\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\nexport interface ThemeProviderProps {\n children: React.ReactNode;\n /** Default theme mode for uncontrolled usage */\n defaultMode?: ThemeMode;\n /** Alias for defaultMode (more intuitive naming) */\n defaultTheme?: ThemeMode;\n /** Controlled theme mode */\n mode?: ThemeMode;\n /** Callback when mode changes */\n onModeChange?: (mode: ThemeMode) => void;\n /** localStorage key for persistence (default: 'fui-theme') */\n storageKey?: string;\n /** How to apply theme to DOM */\n attribute?: 'data-theme' | 'class';\n}\n\nexport interface UseThemeReturn {\n /** Current theme mode setting */\n mode: ThemeMode;\n /** Set the theme mode */\n setMode: (mode: ThemeMode) => void;\n /** Resolved mode (never 'system', always 'light' or 'dark') */\n resolvedMode: 'light' | 'dark';\n /** System preference detected from prefers-color-scheme */\n systemPreference: 'light' | 'dark';\n /** Toggle between light and dark (skips system) */\n toggleMode: () => void;\n}\n\nexport interface ThemeToggleProps {\n /** Size of the toggle button */\n size?: 'sm' | 'md' | 'lg';\n /** Whether to include system mode option (default: false) */\n showSystem?: boolean;\n /** Controlled value for custom usage (bypasses theme context) */\n value?: 'light' | 'dark';\n /** Callback when value changes (for controlled usage) */\n onValueChange?: (value: 'light' | 'dark') => void;\n /** Accessible label for the group */\n 'aria-label'?: string;\n /** Additional class name */\n className?: string;\n}\n\n// ============================================\n// Context\n// ============================================\n\ninterface ThemeContextValue {\n mode: ThemeMode;\n setMode: (mode: ThemeMode) => void;\n resolvedMode: 'light' | 'dark';\n systemPreference: 'light' | 'dark';\n toggleMode: () => void;\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\n// ============================================\n// Hooks\n// ============================================\n\n/**\n * Hook to detect system color scheme preference\n */\nfunction useSystemPreference(): 'light' | 'dark' {\n const [preference, setPreference] = React.useState<'light' | 'dark'>('light');\n\n React.useEffect(() => {\n // Check if window is available (SSR safety)\n if (typeof window === 'undefined') return;\n\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\n setPreference(mq.matches ? 'dark' : 'light');\n\n const handler = (e: MediaQueryListEvent) => {\n setPreference(e.matches ? 'dark' : 'light');\n };\n\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, []);\n\n return preference;\n}\n\n/**\n * Hook to access theme context\n */\nfunction useTheme(): UseThemeReturn {\n const context = React.useContext(ThemeContext);\n\n if (!context) {\n // Return safe defaults when used outside provider\n return {\n mode: 'system',\n setMode: () => {},\n resolvedMode: 'light',\n systemPreference: 'light',\n toggleMode: () => {},\n };\n }\n\n return context;\n}\n\n// ============================================\n// Icons\n// ============================================\n\nfunction SunIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z\" />\n </svg>\n );\n}\n\nfunction MoonIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z\" />\n </svg>\n );\n}\n\nfunction MonitorIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M208,40H48A24,24,0,0,0,24,64V176a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V64A24,24,0,0,0,208,40Zm8,136a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V64a8,8,0,0,1,8-8H208a8,8,0,0,1,8,8Zm-48,48a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,224Z\" />\n </svg>\n );\n}\n\n// ============================================\n// Components\n// ============================================\n\n/**\n * ThemeProvider - Provides theme context to children\n * SSR-safe: initializes from localStorage in useEffect\n */\nfunction ThemeProvider({\n children,\n defaultMode,\n defaultTheme,\n mode: controlledMode,\n onModeChange,\n storageKey = 'fui-theme',\n attribute = 'data-theme',\n}: ThemeProviderProps) {\n const systemPreference = useSystemPreference();\n\n // Resolve default: defaultMode takes precedence, then defaultTheme, then 'system'\n const resolvedDefault = defaultMode ?? defaultTheme ?? 'system';\n\n // Initialize with resolvedDefault, then hydrate from localStorage in useEffect\n const [internalMode, setInternalMode] = React.useState<ThemeMode>(resolvedDefault);\n const [mounted, setMounted] = React.useState(false);\n\n // Determine if controlled\n const isControlled = controlledMode !== undefined;\n const mode = isControlled ? controlledMode : internalMode;\n\n // Calculate resolved mode\n const resolvedMode: 'light' | 'dark' = mode === 'system' ? systemPreference : mode;\n\n // Hydrate from localStorage on mount (SSR-safe)\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n\n if (!isControlled && storageKey) {\n const stored = localStorage.getItem(storageKey) as ThemeMode | null;\n if (stored && ['light', 'dark', 'system'].includes(stored)) {\n setInternalMode(stored);\n }\n }\n setMounted(true);\n }, [isControlled, storageKey]);\n\n // Apply theme to DOM — skip until mounted so we don't overwrite\n // the inline script that prevents flash on initial page load\n React.useEffect(() => {\n if (typeof document === 'undefined' || !mounted) return;\n\n const root = document.documentElement;\n\n if (attribute === 'data-theme') {\n root.setAttribute('data-theme', resolvedMode);\n } else if (attribute === 'class') {\n root.classList.remove('light', 'dark');\n root.classList.add(resolvedMode);\n }\n }, [resolvedMode, attribute, mounted]);\n\n // Persist to localStorage when mode changes\n React.useEffect(() => {\n if (typeof window === 'undefined' || !storageKey || !mounted) return;\n localStorage.setItem(storageKey, mode);\n }, [mode, storageKey, mounted]);\n\n const setMode = React.useCallback((newMode: ThemeMode) => {\n if (!isControlled) {\n setInternalMode(newMode);\n }\n onModeChange?.(newMode);\n }, [isControlled, onModeChange]);\n\n const toggleMode = React.useCallback(() => {\n const next = resolvedMode === 'light' ? 'dark' : 'light';\n setMode(next);\n }, [resolvedMode, setMode]);\n\n const contextValue: ThemeContextValue = {\n mode,\n setMode,\n resolvedMode,\n systemPreference,\n toggleMode,\n };\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/**\n * ThemeToggle - Fragmented button group to toggle between light and dark themes\n *\n * Can be used in two modes:\n * 1. Uncontrolled (default): Uses ThemeProvider context to get/set theme\n * 2. Controlled: Pass `value` and `onValueChange` props for custom behavior\n */\nfunction ThemeToggle({\n size = 'md',\n showSystem = false,\n value: controlledValue,\n onValueChange,\n 'aria-label': ariaLabel,\n className,\n}: ThemeToggleProps) {\n const { mode: contextMode, setMode: setContextMode } = useTheme();\n\n // Use controlled value if provided, otherwise use context\n const isControlled = controlledValue !== undefined;\n const currentMode = isControlled ? controlledValue : contextMode;\n\n const handleModeChange = (newMode: 'light' | 'dark') => {\n if (isControlled) {\n onValueChange?.(newMode);\n } else {\n setContextMode(newMode);\n }\n };\n\n const groupClasses = [\n styles.toggleGroup,\n styles[`size${size.charAt(0).toUpperCase() + size.slice(1)}`],\n className,\n ].filter(Boolean).join(' ');\n\n const getButtonClasses = (buttonMode: ThemeMode) => {\n return [\n styles.toggleButton,\n currentMode === buttonMode && styles.toggleButtonActive,\n ].filter(Boolean).join(' ');\n };\n\n const label = ariaLabel || 'Theme toggle';\n\n return (\n <div\n className={groupClasses}\n role=\"group\"\n aria-label={label}\n >\n <button\n type=\"button\"\n className={getButtonClasses('light')}\n onClick={() => handleModeChange('light')}\n aria-pressed={currentMode === 'light'}\n aria-label=\"Light mode\"\n >\n <SunIcon />\n </button>\n <button\n type=\"button\"\n className={getButtonClasses('dark')}\n onClick={() => handleModeChange('dark')}\n aria-pressed={currentMode === 'dark'}\n aria-label=\"Dark mode\"\n >\n <MoonIcon />\n </button>\n {showSystem && !isControlled && (\n <button\n type=\"button\"\n className={getButtonClasses('system')}\n onClick={() => setContextMode('system')}\n aria-pressed={contextMode === 'system'}\n aria-label=\"System preference\"\n >\n <MonitorIcon />\n </button>\n )}\n </div>\n );\n}\n\n// ============================================\n// Exports\n// ============================================\n\nexport const Theme = Object.assign(ThemeProvider, {\n Root: ThemeProvider,\n Provider: ThemeProvider,\n Toggle: ThemeToggle,\n useTheme,\n});\n\nexport { ThemeProvider, ThemeToggle, useTheme };\n"],"names":["React","jsx","styles","jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../src/components/Theme/index.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport styles from './ThemeToggle.module.scss';\n\n// ============================================\n// Types\n// ============================================\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\nexport interface ThemeProviderProps {\n children: React.ReactNode;\n /** Default theme mode for uncontrolled usage */\n defaultMode?: ThemeMode;\n /**\n * @deprecated Use `defaultMode` instead. This alias will be removed in v1.0.\n */\n defaultTheme?: ThemeMode;\n /** Controlled theme mode */\n mode?: ThemeMode;\n /** Callback when mode changes */\n onModeChange?: (mode: ThemeMode) => void;\n /** localStorage key for persistence (default: 'fui-theme') */\n storageKey?: string;\n /** How to apply theme to DOM */\n attribute?: 'data-theme' | 'class';\n}\n\nexport interface UseThemeReturn {\n /** Current theme mode setting */\n mode: ThemeMode;\n /** Set the theme mode */\n setMode: (mode: ThemeMode) => void;\n /** Resolved mode (never 'system', always 'light' or 'dark') */\n resolvedMode: 'light' | 'dark';\n /** System preference detected from prefers-color-scheme */\n systemPreference: 'light' | 'dark';\n /** Toggle between light and dark (skips system) */\n toggleMode: () => void;\n}\n\nexport interface ThemeToggleProps {\n /** Size of the toggle button */\n size?: 'sm' | 'md' | 'lg';\n /** Whether to include system mode option (default: false) */\n showSystem?: boolean;\n /** Controlled value for custom usage (bypasses theme context) */\n value?: 'light' | 'dark';\n /** Callback when value changes (for controlled usage) */\n onValueChange?: (value: 'light' | 'dark') => void;\n /** Accessible label for the group */\n 'aria-label'?: string;\n /** Additional class name */\n className?: string;\n}\n\n// ============================================\n// Context\n// ============================================\n\ninterface ThemeContextValue {\n mode: ThemeMode;\n setMode: (mode: ThemeMode) => void;\n resolvedMode: 'light' | 'dark';\n systemPreference: 'light' | 'dark';\n toggleMode: () => void;\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\n// ============================================\n// Hooks\n// ============================================\n\n/**\n * Hook to detect system color scheme preference\n */\nfunction useSystemPreference(): 'light' | 'dark' {\n const [preference, setPreference] = React.useState<'light' | 'dark'>('light');\n\n React.useEffect(() => {\n // Check if window is available (SSR safety)\n if (typeof window === 'undefined') return;\n\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\n setPreference(mq.matches ? 'dark' : 'light');\n\n const handler = (e: MediaQueryListEvent) => {\n setPreference(e.matches ? 'dark' : 'light');\n };\n\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, []);\n\n return preference;\n}\n\n/**\n * Hook to access theme context\n */\nfunction useTheme(): UseThemeReturn {\n const context = React.useContext(ThemeContext);\n\n if (!context) {\n // Return safe defaults when used outside provider\n return {\n mode: 'system',\n setMode: () => {},\n resolvedMode: 'light',\n systemPreference: 'light',\n toggleMode: () => {},\n };\n }\n\n return context;\n}\n\n// ============================================\n// Icons\n// ============================================\n\nfunction SunIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z\" />\n </svg>\n );\n}\n\nfunction MoonIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z\" />\n </svg>\n );\n}\n\nfunction MonitorIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M208,40H48A24,24,0,0,0,24,64V176a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V64A24,24,0,0,0,208,40Zm8,136a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V64a8,8,0,0,1,8-8H208a8,8,0,0,1,8,8Zm-48,48a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,224Z\" />\n </svg>\n );\n}\n\n// ============================================\n// Components\n// ============================================\n\n/**\n * ThemeProvider - Provides theme context to children\n * SSR-safe: initializes from localStorage in useEffect\n */\nfunction ThemeProvider({\n children,\n defaultMode,\n defaultTheme,\n mode: controlledMode,\n onModeChange,\n storageKey = 'fui-theme',\n attribute = 'data-theme',\n}: ThemeProviderProps) {\n const systemPreference = useSystemPreference();\n\n // Warn on deprecated prop usage (dev only)\n if (process.env.NODE_ENV !== 'production' && defaultTheme !== undefined) {\n console.warn(\n '[Fragments] ThemeProvider: `defaultTheme` is deprecated. Use `defaultMode` instead. ' +\n '`defaultTheme` will be removed in v1.0.'\n );\n }\n\n // Resolve default: defaultMode takes precedence, then defaultTheme, then 'system'\n const resolvedDefault = defaultMode ?? defaultTheme ?? 'system';\n\n // Initialize with resolvedDefault, then hydrate from localStorage in useEffect\n const [internalMode, setInternalMode] = React.useState<ThemeMode>(resolvedDefault);\n const [mounted, setMounted] = React.useState(false);\n\n // Determine if controlled\n const isControlled = controlledMode !== undefined;\n const mode = isControlled ? controlledMode : internalMode;\n\n // Calculate resolved mode\n const resolvedMode: 'light' | 'dark' = mode === 'system' ? systemPreference : mode;\n\n // Hydrate from localStorage on mount (SSR-safe)\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n\n if (!isControlled && storageKey) {\n const stored = localStorage.getItem(storageKey) as ThemeMode | null;\n if (stored && ['light', 'dark', 'system'].includes(stored)) {\n setInternalMode(stored);\n }\n }\n setMounted(true);\n }, [isControlled, storageKey]);\n\n // Apply theme to DOM — skip until mounted so we don't overwrite\n // the inline script that prevents flash on initial page load\n React.useEffect(() => {\n if (typeof document === 'undefined' || !mounted) return;\n\n const root = document.documentElement;\n\n if (attribute === 'data-theme') {\n root.setAttribute('data-theme', resolvedMode);\n } else if (attribute === 'class') {\n root.classList.remove('light', 'dark');\n root.classList.add(resolvedMode);\n }\n }, [resolvedMode, attribute, mounted]);\n\n // Persist to localStorage when mode changes\n React.useEffect(() => {\n if (typeof window === 'undefined' || !storageKey || !mounted) return;\n localStorage.setItem(storageKey, mode);\n }, [mode, storageKey, mounted]);\n\n const setMode = React.useCallback((newMode: ThemeMode) => {\n if (!isControlled) {\n setInternalMode(newMode);\n }\n onModeChange?.(newMode);\n }, [isControlled, onModeChange]);\n\n const toggleMode = React.useCallback(() => {\n const next = resolvedMode === 'light' ? 'dark' : 'light';\n setMode(next);\n }, [resolvedMode, setMode]);\n\n const contextValue: ThemeContextValue = {\n mode,\n setMode,\n resolvedMode,\n systemPreference,\n toggleMode,\n };\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/**\n * ThemeToggle - Fragmented button group to toggle between light and dark themes\n *\n * Can be used in two modes:\n * 1. Uncontrolled (default): Uses ThemeProvider context to get/set theme\n * 2. Controlled: Pass `value` and `onValueChange` props for custom behavior\n */\nfunction ThemeToggle({\n size = 'md',\n showSystem = false,\n value: controlledValue,\n onValueChange,\n 'aria-label': ariaLabel,\n className,\n}: ThemeToggleProps) {\n const { mode: contextMode, setMode: setContextMode } = useTheme();\n\n // Use controlled value if provided, otherwise use context\n const isControlled = controlledValue !== undefined;\n const currentMode = isControlled ? controlledValue : contextMode;\n\n const handleModeChange = (newMode: 'light' | 'dark') => {\n if (isControlled) {\n onValueChange?.(newMode);\n } else {\n setContextMode(newMode);\n }\n };\n\n const groupClasses = [\n styles.toggleGroup,\n styles[`size${size.charAt(0).toUpperCase() + size.slice(1)}`],\n className,\n ].filter(Boolean).join(' ');\n\n const getButtonClasses = (buttonMode: ThemeMode) => {\n return [\n styles.toggleButton,\n currentMode === buttonMode && styles.toggleButtonActive,\n ].filter(Boolean).join(' ');\n };\n\n const label = ariaLabel || 'Theme toggle';\n\n return (\n <div\n className={groupClasses}\n role=\"group\"\n aria-label={label}\n >\n <button\n type=\"button\"\n className={getButtonClasses('light')}\n onClick={() => handleModeChange('light')}\n aria-pressed={currentMode === 'light'}\n aria-label=\"Light mode\"\n >\n <SunIcon />\n </button>\n <button\n type=\"button\"\n className={getButtonClasses('dark')}\n onClick={() => handleModeChange('dark')}\n aria-pressed={currentMode === 'dark'}\n aria-label=\"Dark mode\"\n >\n <MoonIcon />\n </button>\n {showSystem && !isControlled && (\n <button\n type=\"button\"\n className={getButtonClasses('system')}\n onClick={() => setContextMode('system')}\n aria-pressed={contextMode === 'system'}\n aria-label=\"System preference\"\n >\n <MonitorIcon />\n </button>\n )}\n </div>\n );\n}\n\n// ============================================\n// Exports\n// ============================================\n\nexport const Theme = Object.assign(ThemeProvider, {\n Root: ThemeProvider,\n Provider: ThemeProvider,\n Toggle: ThemeToggle,\n useTheme,\n});\n\nexport { ThemeProvider, ThemeToggle, useTheme };\n\n// ============================================\n// configureTheme — JS-only seed configuration\n// ============================================\n\nexport type NeutralPalette = 'stone' | 'ice' | 'earth' | 'sand' | 'fire';\nexport type DensityPreset = 'compact' | 'default' | 'relaxed';\nexport type RadiusStyle = 'sharp' | 'subtle' | 'default' | 'rounded' | 'pill';\n\nexport interface ConfigureThemeOptions {\n /** Brand/accent color as hex */\n brand?: string;\n /** Neutral palette name */\n neutral?: NeutralPalette;\n /** Spacing density preset */\n density?: DensityPreset;\n /** Border radius style */\n radiusStyle?: RadiusStyle;\n /** Danger/error color as hex */\n danger?: string;\n /** Success color as hex */\n success?: string;\n /** Warning color as hex */\n warning?: string;\n /** Info color as hex */\n info?: string;\n}\n\n// -- Radius presets (match _radius.scss) --\n\nconst RADIUS_PRESETS: Record<RadiusStyle, Record<string, string>> = {\n sharp: { sm: '0', md: '0', lg: '0', xl: '0' },\n subtle: { sm: '0.125rem', md: '0.25rem', lg: '0.375rem', xl: '0.5rem' },\n default: { sm: '0.25rem', md: '0.429rem', lg: '0.571rem', xl: '0.857rem' },\n rounded: { sm: '0.375rem', md: '0.5rem', lg: '0.75rem', xl: '1rem' },\n pill: { sm: '0.5rem', md: '0.75rem', lg: '1rem', xl: '1.5rem' },\n};\n\n// -- Density presets (match _density.scss) --\n\ninterface DensityConfig {\n baseUnit: number;\n baseFontSize: number;\n buttonHeights: [number, number, number]; // sm, md, lg\n inputHeights: [number, number, number]; // sm, default, lg\n}\n\nconst DENSITY_CONFIGS: Record<DensityPreset, DensityConfig> = {\n compact: { baseUnit: 6, baseFontSize: 14, buttonHeights: [24, 30, 36], inputHeights: [24, 32, 36] },\n default: { baseUnit: 7, baseFontSize: 14, buttonHeights: [28, 36, 44], inputHeights: [28, 40, 44] },\n relaxed: { baseUnit: 8, baseFontSize: 14, buttonHeights: [32, 40, 48], inputHeights: [32, 44, 48] },\n};\n\nfunction pxToRem(px: number, baseFontSize: number): string {\n return `${px / baseFontSize}rem`;\n}\n\nfunction hexToRgb(hex: string): [number, number, number] | null {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (!result) return null;\n return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];\n}\n\nfunction adjustLightness(hex: string, amount: number): string {\n const rgb = hexToRgb(hex);\n if (!rgb) return hex;\n const [r, g, b] = rgb;\n const adjust = (v: number) => Math.max(0, Math.min(255, Math.round(v + amount)));\n return `#${adjust(r).toString(16).padStart(2, '0')}${adjust(g).toString(16).padStart(2, '0')}${adjust(b).toString(16).padStart(2, '0')}`;\n}\n\nfunction setVar(el: HTMLElement, name: string, value: string) {\n el.style.setProperty(name, value);\n}\n\n/**\n * Configure theme seeds at runtime via JS. Sets CSS custom properties on\n * `:root` without requiring SCSS. Call this once at app startup.\n *\n * Note: For full control over all 120+ tokens, use the SCSS `@use...with()`\n * approach. `configureTheme` covers the most commonly customized tokens.\n *\n * @example\n * ```ts\n * import { configureTheme } from '@fragments-sdk/ui';\n *\n * configureTheme({\n * brand: '#6366f1',\n * neutral: 'ice',\n * density: 'compact',\n * radiusStyle: 'rounded',\n * });\n * ```\n */\nexport function configureTheme(options: ConfigureThemeOptions): void {\n if (typeof document === 'undefined') return;\n\n const root = document.documentElement;\n\n // -- Brand / Accent --\n if (options.brand) {\n setVar(root, '--fui-color-accent', options.brand);\n setVar(root, '--fui-color-accent-hover', adjustLightness(options.brand, -20));\n setVar(root, '--fui-color-accent-active', adjustLightness(options.brand, -40));\n setVar(root, '--fui-focus-ring-color', `${options.brand}66`); // 40% alpha\n }\n\n // -- Semantic colors --\n if (options.danger) {\n setVar(root, '--fui-color-danger', options.danger);\n setVar(root, '--fui-color-danger-hover', adjustLightness(options.danger, -20));\n }\n if (options.success) setVar(root, '--fui-color-success', options.success);\n if (options.warning) setVar(root, '--fui-color-warning', options.warning);\n if (options.info) setVar(root, '--fui-color-info', options.info);\n\n // -- Radius --\n if (options.radiusStyle) {\n const r = RADIUS_PRESETS[options.radiusStyle];\n if (r) {\n setVar(root, '--fui-radius-sm', r.sm);\n setVar(root, '--fui-radius-md', r.md);\n setVar(root, '--fui-radius-lg', r.lg);\n setVar(root, '--fui-radius-xl', r.xl);\n }\n }\n\n // -- Density --\n if (options.density) {\n const d = DENSITY_CONFIGS[options.density];\n if (d) {\n const unitRem = d.baseUnit / d.baseFontSize;\n\n // Spacing scale\n setVar(root, '--fui-space-1', `${unitRem}rem`);\n setVar(root, '--fui-space-2', `${unitRem * 2}rem`);\n setVar(root, '--fui-space-3', `${unitRem * 3}rem`);\n setVar(root, '--fui-space-4', `${unitRem * 4}rem`);\n setVar(root, '--fui-space-5', `${unitRem * 5}rem`);\n setVar(root, '--fui-space-6', `${unitRem * 6}rem`);\n setVar(root, '--fui-space-8', `${unitRem * 8}rem`);\n setVar(root, '--fui-space-10', `${unitRem * 10}rem`);\n setVar(root, '--fui-space-12', `${unitRem * 12}rem`);\n\n // Component heights\n setVar(root, '--fui-button-height-sm', pxToRem(d.buttonHeights[0], d.baseFontSize));\n setVar(root, '--fui-button-height-md', pxToRem(d.buttonHeights[1], d.baseFontSize));\n setVar(root, '--fui-button-height-lg', pxToRem(d.buttonHeights[2], d.baseFontSize));\n setVar(root, '--fui-input-height-sm', pxToRem(d.inputHeights[0], d.baseFontSize));\n setVar(root, '--fui-input-height', pxToRem(d.inputHeights[1], d.baseFontSize));\n setVar(root, '--fui-input-height-lg', pxToRem(d.inputHeights[2], d.baseFontSize));\n\n // Base unit\n setVar(root, '--fui-base-unit', `${d.baseUnit}px`);\n }\n }\n}\n"],"names":["React","jsx","styles","jsxs"],"mappings":"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"}
|
|
@@ -4,7 +4,9 @@ export interface ThemeProviderProps {
|
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
/** Default theme mode for uncontrolled usage */
|
|
6
6
|
defaultMode?: ThemeMode;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Use `defaultMode` instead. This alias will be removed in v1.0.
|
|
9
|
+
*/
|
|
8
10
|
defaultTheme?: ThemeMode;
|
|
9
11
|
/** Controlled theme mode */
|
|
10
12
|
mode?: ThemeMode;
|
|
@@ -65,4 +67,45 @@ export declare const Theme: typeof ThemeProvider & {
|
|
|
65
67
|
useTheme: typeof useTheme;
|
|
66
68
|
};
|
|
67
69
|
export { ThemeProvider, ThemeToggle, useTheme };
|
|
70
|
+
export type NeutralPalette = 'stone' | 'ice' | 'earth' | 'sand' | 'fire';
|
|
71
|
+
export type DensityPreset = 'compact' | 'default' | 'relaxed';
|
|
72
|
+
export type RadiusStyle = 'sharp' | 'subtle' | 'default' | 'rounded' | 'pill';
|
|
73
|
+
export interface ConfigureThemeOptions {
|
|
74
|
+
/** Brand/accent color as hex */
|
|
75
|
+
brand?: string;
|
|
76
|
+
/** Neutral palette name */
|
|
77
|
+
neutral?: NeutralPalette;
|
|
78
|
+
/** Spacing density preset */
|
|
79
|
+
density?: DensityPreset;
|
|
80
|
+
/** Border radius style */
|
|
81
|
+
radiusStyle?: RadiusStyle;
|
|
82
|
+
/** Danger/error color as hex */
|
|
83
|
+
danger?: string;
|
|
84
|
+
/** Success color as hex */
|
|
85
|
+
success?: string;
|
|
86
|
+
/** Warning color as hex */
|
|
87
|
+
warning?: string;
|
|
88
|
+
/** Info color as hex */
|
|
89
|
+
info?: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Configure theme seeds at runtime via JS. Sets CSS custom properties on
|
|
93
|
+
* `:root` without requiring SCSS. Call this once at app startup.
|
|
94
|
+
*
|
|
95
|
+
* Note: For full control over all 120+ tokens, use the SCSS `@use...with()`
|
|
96
|
+
* approach. `configureTheme` covers the most commonly customized tokens.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { configureTheme } from '@fragments-sdk/ui';
|
|
101
|
+
*
|
|
102
|
+
* configureTheme({
|
|
103
|
+
* brand: '#6366f1',
|
|
104
|
+
* neutral: 'ice',
|
|
105
|
+
* density: 'compact',
|
|
106
|
+
* radiusStyle: 'rounded',
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare function configureTheme(options: ConfigureThemeOptions): void;
|
|
68
111
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Theme/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,gDAAgD;IAChD,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Theme/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -83,6 +83,11 @@ function ThemeProvider({
|
|
|
83
83
|
attribute = "data-theme"
|
|
84
84
|
}) {
|
|
85
85
|
const systemPreference = useSystemPreference();
|
|
86
|
+
if (process.env.NODE_ENV !== "production" && defaultTheme !== void 0) {
|
|
87
|
+
console.warn(
|
|
88
|
+
"[Fragments] ThemeProvider: `defaultTheme` is deprecated. Use `defaultMode` instead. `defaultTheme` will be removed in v1.0."
|
|
89
|
+
);
|
|
90
|
+
}
|
|
86
91
|
const resolvedDefault = defaultMode ?? defaultTheme ?? "system";
|
|
87
92
|
const [internalMode, setInternalMode] = React.useState(resolvedDefault);
|
|
88
93
|
const [mounted, setMounted] = React.useState(false);
|
|
@@ -206,15 +211,95 @@ function ThemeToggle({
|
|
|
206
211
|
}
|
|
207
212
|
);
|
|
208
213
|
}
|
|
209
|
-
Object.assign(ThemeProvider, {
|
|
214
|
+
const Theme = Object.assign(ThemeProvider, {
|
|
210
215
|
Root: ThemeProvider,
|
|
211
216
|
Provider: ThemeProvider,
|
|
212
217
|
Toggle: ThemeToggle,
|
|
213
218
|
useTheme
|
|
214
219
|
});
|
|
220
|
+
const RADIUS_PRESETS = {
|
|
221
|
+
sharp: { sm: "0", md: "0", lg: "0", xl: "0" },
|
|
222
|
+
subtle: { sm: "0.125rem", md: "0.25rem", lg: "0.375rem", xl: "0.5rem" },
|
|
223
|
+
default: { sm: "0.25rem", md: "0.429rem", lg: "0.571rem", xl: "0.857rem" },
|
|
224
|
+
rounded: { sm: "0.375rem", md: "0.5rem", lg: "0.75rem", xl: "1rem" },
|
|
225
|
+
pill: { sm: "0.5rem", md: "0.75rem", lg: "1rem", xl: "1.5rem" }
|
|
226
|
+
};
|
|
227
|
+
const DENSITY_CONFIGS = {
|
|
228
|
+
compact: { baseUnit: 6, baseFontSize: 14, buttonHeights: [24, 30, 36], inputHeights: [24, 32, 36] },
|
|
229
|
+
default: { baseUnit: 7, baseFontSize: 14, buttonHeights: [28, 36, 44], inputHeights: [28, 40, 44] },
|
|
230
|
+
relaxed: { baseUnit: 8, baseFontSize: 14, buttonHeights: [32, 40, 48], inputHeights: [32, 44, 48] }
|
|
231
|
+
};
|
|
232
|
+
function pxToRem(px, baseFontSize) {
|
|
233
|
+
return `${px / baseFontSize}rem`;
|
|
234
|
+
}
|
|
235
|
+
function hexToRgb(hex) {
|
|
236
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
237
|
+
if (!result) return null;
|
|
238
|
+
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
|
239
|
+
}
|
|
240
|
+
function adjustLightness(hex, amount) {
|
|
241
|
+
const rgb = hexToRgb(hex);
|
|
242
|
+
if (!rgb) return hex;
|
|
243
|
+
const [r, g, b] = rgb;
|
|
244
|
+
const adjust = (v) => Math.max(0, Math.min(255, Math.round(v + amount)));
|
|
245
|
+
return `#${adjust(r).toString(16).padStart(2, "0")}${adjust(g).toString(16).padStart(2, "0")}${adjust(b).toString(16).padStart(2, "0")}`;
|
|
246
|
+
}
|
|
247
|
+
function setVar(el, name, value) {
|
|
248
|
+
el.style.setProperty(name, value);
|
|
249
|
+
}
|
|
250
|
+
function configureTheme(options) {
|
|
251
|
+
if (typeof document === "undefined") return;
|
|
252
|
+
const root = document.documentElement;
|
|
253
|
+
if (options.brand) {
|
|
254
|
+
setVar(root, "--fui-color-accent", options.brand);
|
|
255
|
+
setVar(root, "--fui-color-accent-hover", adjustLightness(options.brand, -20));
|
|
256
|
+
setVar(root, "--fui-color-accent-active", adjustLightness(options.brand, -40));
|
|
257
|
+
setVar(root, "--fui-focus-ring-color", `${options.brand}66`);
|
|
258
|
+
}
|
|
259
|
+
if (options.danger) {
|
|
260
|
+
setVar(root, "--fui-color-danger", options.danger);
|
|
261
|
+
setVar(root, "--fui-color-danger-hover", adjustLightness(options.danger, -20));
|
|
262
|
+
}
|
|
263
|
+
if (options.success) setVar(root, "--fui-color-success", options.success);
|
|
264
|
+
if (options.warning) setVar(root, "--fui-color-warning", options.warning);
|
|
265
|
+
if (options.info) setVar(root, "--fui-color-info", options.info);
|
|
266
|
+
if (options.radiusStyle) {
|
|
267
|
+
const r = RADIUS_PRESETS[options.radiusStyle];
|
|
268
|
+
if (r) {
|
|
269
|
+
setVar(root, "--fui-radius-sm", r.sm);
|
|
270
|
+
setVar(root, "--fui-radius-md", r.md);
|
|
271
|
+
setVar(root, "--fui-radius-lg", r.lg);
|
|
272
|
+
setVar(root, "--fui-radius-xl", r.xl);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (options.density) {
|
|
276
|
+
const d = DENSITY_CONFIGS[options.density];
|
|
277
|
+
if (d) {
|
|
278
|
+
const unitRem = d.baseUnit / d.baseFontSize;
|
|
279
|
+
setVar(root, "--fui-space-1", `${unitRem}rem`);
|
|
280
|
+
setVar(root, "--fui-space-2", `${unitRem * 2}rem`);
|
|
281
|
+
setVar(root, "--fui-space-3", `${unitRem * 3}rem`);
|
|
282
|
+
setVar(root, "--fui-space-4", `${unitRem * 4}rem`);
|
|
283
|
+
setVar(root, "--fui-space-5", `${unitRem * 5}rem`);
|
|
284
|
+
setVar(root, "--fui-space-6", `${unitRem * 6}rem`);
|
|
285
|
+
setVar(root, "--fui-space-8", `${unitRem * 8}rem`);
|
|
286
|
+
setVar(root, "--fui-space-10", `${unitRem * 10}rem`);
|
|
287
|
+
setVar(root, "--fui-space-12", `${unitRem * 12}rem`);
|
|
288
|
+
setVar(root, "--fui-button-height-sm", pxToRem(d.buttonHeights[0], d.baseFontSize));
|
|
289
|
+
setVar(root, "--fui-button-height-md", pxToRem(d.buttonHeights[1], d.baseFontSize));
|
|
290
|
+
setVar(root, "--fui-button-height-lg", pxToRem(d.buttonHeights[2], d.baseFontSize));
|
|
291
|
+
setVar(root, "--fui-input-height-sm", pxToRem(d.inputHeights[0], d.baseFontSize));
|
|
292
|
+
setVar(root, "--fui-input-height", pxToRem(d.inputHeights[1], d.baseFontSize));
|
|
293
|
+
setVar(root, "--fui-input-height-lg", pxToRem(d.inputHeights[2], d.baseFontSize));
|
|
294
|
+
setVar(root, "--fui-base-unit", `${d.baseUnit}px`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
215
298
|
export {
|
|
299
|
+
Theme,
|
|
216
300
|
ThemeProvider,
|
|
217
301
|
ThemeToggle,
|
|
302
|
+
configureTheme,
|
|
218
303
|
useTheme
|
|
219
304
|
};
|
|
220
305
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/components/Theme/index.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport styles from './ThemeToggle.module.scss';\n\n// ============================================\n// Types\n// ============================================\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\nexport interface ThemeProviderProps {\n children: React.ReactNode;\n /** Default theme mode for uncontrolled usage */\n defaultMode?: ThemeMode;\n /** Alias for defaultMode (more intuitive naming) */\n defaultTheme?: ThemeMode;\n /** Controlled theme mode */\n mode?: ThemeMode;\n /** Callback when mode changes */\n onModeChange?: (mode: ThemeMode) => void;\n /** localStorage key for persistence (default: 'fui-theme') */\n storageKey?: string;\n /** How to apply theme to DOM */\n attribute?: 'data-theme' | 'class';\n}\n\nexport interface UseThemeReturn {\n /** Current theme mode setting */\n mode: ThemeMode;\n /** Set the theme mode */\n setMode: (mode: ThemeMode) => void;\n /** Resolved mode (never 'system', always 'light' or 'dark') */\n resolvedMode: 'light' | 'dark';\n /** System preference detected from prefers-color-scheme */\n systemPreference: 'light' | 'dark';\n /** Toggle between light and dark (skips system) */\n toggleMode: () => void;\n}\n\nexport interface ThemeToggleProps {\n /** Size of the toggle button */\n size?: 'sm' | 'md' | 'lg';\n /** Whether to include system mode option (default: false) */\n showSystem?: boolean;\n /** Controlled value for custom usage (bypasses theme context) */\n value?: 'light' | 'dark';\n /** Callback when value changes (for controlled usage) */\n onValueChange?: (value: 'light' | 'dark') => void;\n /** Accessible label for the group */\n 'aria-label'?: string;\n /** Additional class name */\n className?: string;\n}\n\n// ============================================\n// Context\n// ============================================\n\ninterface ThemeContextValue {\n mode: ThemeMode;\n setMode: (mode: ThemeMode) => void;\n resolvedMode: 'light' | 'dark';\n systemPreference: 'light' | 'dark';\n toggleMode: () => void;\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\n// ============================================\n// Hooks\n// ============================================\n\n/**\n * Hook to detect system color scheme preference\n */\nfunction useSystemPreference(): 'light' | 'dark' {\n const [preference, setPreference] = React.useState<'light' | 'dark'>('light');\n\n React.useEffect(() => {\n // Check if window is available (SSR safety)\n if (typeof window === 'undefined') return;\n\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\n setPreference(mq.matches ? 'dark' : 'light');\n\n const handler = (e: MediaQueryListEvent) => {\n setPreference(e.matches ? 'dark' : 'light');\n };\n\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, []);\n\n return preference;\n}\n\n/**\n * Hook to access theme context\n */\nfunction useTheme(): UseThemeReturn {\n const context = React.useContext(ThemeContext);\n\n if (!context) {\n // Return safe defaults when used outside provider\n return {\n mode: 'system',\n setMode: () => {},\n resolvedMode: 'light',\n systemPreference: 'light',\n toggleMode: () => {},\n };\n }\n\n return context;\n}\n\n// ============================================\n// Icons\n// ============================================\n\nfunction SunIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z\" />\n </svg>\n );\n}\n\nfunction MoonIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z\" />\n </svg>\n );\n}\n\nfunction MonitorIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M208,40H48A24,24,0,0,0,24,64V176a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V64A24,24,0,0,0,208,40Zm8,136a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V64a8,8,0,0,1,8-8H208a8,8,0,0,1,8,8Zm-48,48a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,224Z\" />\n </svg>\n );\n}\n\n// ============================================\n// Components\n// ============================================\n\n/**\n * ThemeProvider - Provides theme context to children\n * SSR-safe: initializes from localStorage in useEffect\n */\nfunction ThemeProvider({\n children,\n defaultMode,\n defaultTheme,\n mode: controlledMode,\n onModeChange,\n storageKey = 'fui-theme',\n attribute = 'data-theme',\n}: ThemeProviderProps) {\n const systemPreference = useSystemPreference();\n\n // Resolve default: defaultMode takes precedence, then defaultTheme, then 'system'\n const resolvedDefault = defaultMode ?? defaultTheme ?? 'system';\n\n // Initialize with resolvedDefault, then hydrate from localStorage in useEffect\n const [internalMode, setInternalMode] = React.useState<ThemeMode>(resolvedDefault);\n const [mounted, setMounted] = React.useState(false);\n\n // Determine if controlled\n const isControlled = controlledMode !== undefined;\n const mode = isControlled ? controlledMode : internalMode;\n\n // Calculate resolved mode\n const resolvedMode: 'light' | 'dark' = mode === 'system' ? systemPreference : mode;\n\n // Hydrate from localStorage on mount (SSR-safe)\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n\n if (!isControlled && storageKey) {\n const stored = localStorage.getItem(storageKey) as ThemeMode | null;\n if (stored && ['light', 'dark', 'system'].includes(stored)) {\n setInternalMode(stored);\n }\n }\n setMounted(true);\n }, [isControlled, storageKey]);\n\n // Apply theme to DOM — skip until mounted so we don't overwrite\n // the inline script that prevents flash on initial page load\n React.useEffect(() => {\n if (typeof document === 'undefined' || !mounted) return;\n\n const root = document.documentElement;\n\n if (attribute === 'data-theme') {\n root.setAttribute('data-theme', resolvedMode);\n } else if (attribute === 'class') {\n root.classList.remove('light', 'dark');\n root.classList.add(resolvedMode);\n }\n }, [resolvedMode, attribute, mounted]);\n\n // Persist to localStorage when mode changes\n React.useEffect(() => {\n if (typeof window === 'undefined' || !storageKey || !mounted) return;\n localStorage.setItem(storageKey, mode);\n }, [mode, storageKey, mounted]);\n\n const setMode = React.useCallback((newMode: ThemeMode) => {\n if (!isControlled) {\n setInternalMode(newMode);\n }\n onModeChange?.(newMode);\n }, [isControlled, onModeChange]);\n\n const toggleMode = React.useCallback(() => {\n const next = resolvedMode === 'light' ? 'dark' : 'light';\n setMode(next);\n }, [resolvedMode, setMode]);\n\n const contextValue: ThemeContextValue = {\n mode,\n setMode,\n resolvedMode,\n systemPreference,\n toggleMode,\n };\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/**\n * ThemeToggle - Fragmented button group to toggle between light and dark themes\n *\n * Can be used in two modes:\n * 1. Uncontrolled (default): Uses ThemeProvider context to get/set theme\n * 2. Controlled: Pass `value` and `onValueChange` props for custom behavior\n */\nfunction ThemeToggle({\n size = 'md',\n showSystem = false,\n value: controlledValue,\n onValueChange,\n 'aria-label': ariaLabel,\n className,\n}: ThemeToggleProps) {\n const { mode: contextMode, setMode: setContextMode } = useTheme();\n\n // Use controlled value if provided, otherwise use context\n const isControlled = controlledValue !== undefined;\n const currentMode = isControlled ? controlledValue : contextMode;\n\n const handleModeChange = (newMode: 'light' | 'dark') => {\n if (isControlled) {\n onValueChange?.(newMode);\n } else {\n setContextMode(newMode);\n }\n };\n\n const groupClasses = [\n styles.toggleGroup,\n styles[`size${size.charAt(0).toUpperCase() + size.slice(1)}`],\n className,\n ].filter(Boolean).join(' ');\n\n const getButtonClasses = (buttonMode: ThemeMode) => {\n return [\n styles.toggleButton,\n currentMode === buttonMode && styles.toggleButtonActive,\n ].filter(Boolean).join(' ');\n };\n\n const label = ariaLabel || 'Theme toggle';\n\n return (\n <div\n className={groupClasses}\n role=\"group\"\n aria-label={label}\n >\n <button\n type=\"button\"\n className={getButtonClasses('light')}\n onClick={() => handleModeChange('light')}\n aria-pressed={currentMode === 'light'}\n aria-label=\"Light mode\"\n >\n <SunIcon />\n </button>\n <button\n type=\"button\"\n className={getButtonClasses('dark')}\n onClick={() => handleModeChange('dark')}\n aria-pressed={currentMode === 'dark'}\n aria-label=\"Dark mode\"\n >\n <MoonIcon />\n </button>\n {showSystem && !isControlled && (\n <button\n type=\"button\"\n className={getButtonClasses('system')}\n onClick={() => setContextMode('system')}\n aria-pressed={contextMode === 'system'}\n aria-label=\"System preference\"\n >\n <MonitorIcon />\n </button>\n )}\n </div>\n );\n}\n\n// ============================================\n// Exports\n// ============================================\n\nexport const Theme = Object.assign(ThemeProvider, {\n Root: ThemeProvider,\n Provider: ThemeProvider,\n Toggle: ThemeToggle,\n useTheme,\n});\n\nexport { ThemeProvider, ThemeToggle, useTheme };\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/components/Theme/index.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport styles from './ThemeToggle.module.scss';\n\n// ============================================\n// Types\n// ============================================\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\nexport interface ThemeProviderProps {\n children: React.ReactNode;\n /** Default theme mode for uncontrolled usage */\n defaultMode?: ThemeMode;\n /**\n * @deprecated Use `defaultMode` instead. This alias will be removed in v1.0.\n */\n defaultTheme?: ThemeMode;\n /** Controlled theme mode */\n mode?: ThemeMode;\n /** Callback when mode changes */\n onModeChange?: (mode: ThemeMode) => void;\n /** localStorage key for persistence (default: 'fui-theme') */\n storageKey?: string;\n /** How to apply theme to DOM */\n attribute?: 'data-theme' | 'class';\n}\n\nexport interface UseThemeReturn {\n /** Current theme mode setting */\n mode: ThemeMode;\n /** Set the theme mode */\n setMode: (mode: ThemeMode) => void;\n /** Resolved mode (never 'system', always 'light' or 'dark') */\n resolvedMode: 'light' | 'dark';\n /** System preference detected from prefers-color-scheme */\n systemPreference: 'light' | 'dark';\n /** Toggle between light and dark (skips system) */\n toggleMode: () => void;\n}\n\nexport interface ThemeToggleProps {\n /** Size of the toggle button */\n size?: 'sm' | 'md' | 'lg';\n /** Whether to include system mode option (default: false) */\n showSystem?: boolean;\n /** Controlled value for custom usage (bypasses theme context) */\n value?: 'light' | 'dark';\n /** Callback when value changes (for controlled usage) */\n onValueChange?: (value: 'light' | 'dark') => void;\n /** Accessible label for the group */\n 'aria-label'?: string;\n /** Additional class name */\n className?: string;\n}\n\n// ============================================\n// Context\n// ============================================\n\ninterface ThemeContextValue {\n mode: ThemeMode;\n setMode: (mode: ThemeMode) => void;\n resolvedMode: 'light' | 'dark';\n systemPreference: 'light' | 'dark';\n toggleMode: () => void;\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\n// ============================================\n// Hooks\n// ============================================\n\n/**\n * Hook to detect system color scheme preference\n */\nfunction useSystemPreference(): 'light' | 'dark' {\n const [preference, setPreference] = React.useState<'light' | 'dark'>('light');\n\n React.useEffect(() => {\n // Check if window is available (SSR safety)\n if (typeof window === 'undefined') return;\n\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\n setPreference(mq.matches ? 'dark' : 'light');\n\n const handler = (e: MediaQueryListEvent) => {\n setPreference(e.matches ? 'dark' : 'light');\n };\n\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, []);\n\n return preference;\n}\n\n/**\n * Hook to access theme context\n */\nfunction useTheme(): UseThemeReturn {\n const context = React.useContext(ThemeContext);\n\n if (!context) {\n // Return safe defaults when used outside provider\n return {\n mode: 'system',\n setMode: () => {},\n resolvedMode: 'light',\n systemPreference: 'light',\n toggleMode: () => {},\n };\n }\n\n return context;\n}\n\n// ============================================\n// Icons\n// ============================================\n\nfunction SunIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z\" />\n </svg>\n );\n}\n\nfunction MoonIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z\" />\n </svg>\n );\n}\n\nfunction MonitorIcon({ size = 20 }: { size?: number }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 256 256\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M208,40H48A24,24,0,0,0,24,64V176a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V64A24,24,0,0,0,208,40Zm8,136a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V64a8,8,0,0,1,8-8H208a8,8,0,0,1,8,8Zm-48,48a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,224Z\" />\n </svg>\n );\n}\n\n// ============================================\n// Components\n// ============================================\n\n/**\n * ThemeProvider - Provides theme context to children\n * SSR-safe: initializes from localStorage in useEffect\n */\nfunction ThemeProvider({\n children,\n defaultMode,\n defaultTheme,\n mode: controlledMode,\n onModeChange,\n storageKey = 'fui-theme',\n attribute = 'data-theme',\n}: ThemeProviderProps) {\n const systemPreference = useSystemPreference();\n\n // Warn on deprecated prop usage (dev only)\n if (process.env.NODE_ENV !== 'production' && defaultTheme !== undefined) {\n console.warn(\n '[Fragments] ThemeProvider: `defaultTheme` is deprecated. Use `defaultMode` instead. ' +\n '`defaultTheme` will be removed in v1.0.'\n );\n }\n\n // Resolve default: defaultMode takes precedence, then defaultTheme, then 'system'\n const resolvedDefault = defaultMode ?? defaultTheme ?? 'system';\n\n // Initialize with resolvedDefault, then hydrate from localStorage in useEffect\n const [internalMode, setInternalMode] = React.useState<ThemeMode>(resolvedDefault);\n const [mounted, setMounted] = React.useState(false);\n\n // Determine if controlled\n const isControlled = controlledMode !== undefined;\n const mode = isControlled ? controlledMode : internalMode;\n\n // Calculate resolved mode\n const resolvedMode: 'light' | 'dark' = mode === 'system' ? systemPreference : mode;\n\n // Hydrate from localStorage on mount (SSR-safe)\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n\n if (!isControlled && storageKey) {\n const stored = localStorage.getItem(storageKey) as ThemeMode | null;\n if (stored && ['light', 'dark', 'system'].includes(stored)) {\n setInternalMode(stored);\n }\n }\n setMounted(true);\n }, [isControlled, storageKey]);\n\n // Apply theme to DOM — skip until mounted so we don't overwrite\n // the inline script that prevents flash on initial page load\n React.useEffect(() => {\n if (typeof document === 'undefined' || !mounted) return;\n\n const root = document.documentElement;\n\n if (attribute === 'data-theme') {\n root.setAttribute('data-theme', resolvedMode);\n } else if (attribute === 'class') {\n root.classList.remove('light', 'dark');\n root.classList.add(resolvedMode);\n }\n }, [resolvedMode, attribute, mounted]);\n\n // Persist to localStorage when mode changes\n React.useEffect(() => {\n if (typeof window === 'undefined' || !storageKey || !mounted) return;\n localStorage.setItem(storageKey, mode);\n }, [mode, storageKey, mounted]);\n\n const setMode = React.useCallback((newMode: ThemeMode) => {\n if (!isControlled) {\n setInternalMode(newMode);\n }\n onModeChange?.(newMode);\n }, [isControlled, onModeChange]);\n\n const toggleMode = React.useCallback(() => {\n const next = resolvedMode === 'light' ? 'dark' : 'light';\n setMode(next);\n }, [resolvedMode, setMode]);\n\n const contextValue: ThemeContextValue = {\n mode,\n setMode,\n resolvedMode,\n systemPreference,\n toggleMode,\n };\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\n/**\n * ThemeToggle - Fragmented button group to toggle between light and dark themes\n *\n * Can be used in two modes:\n * 1. Uncontrolled (default): Uses ThemeProvider context to get/set theme\n * 2. Controlled: Pass `value` and `onValueChange` props for custom behavior\n */\nfunction ThemeToggle({\n size = 'md',\n showSystem = false,\n value: controlledValue,\n onValueChange,\n 'aria-label': ariaLabel,\n className,\n}: ThemeToggleProps) {\n const { mode: contextMode, setMode: setContextMode } = useTheme();\n\n // Use controlled value if provided, otherwise use context\n const isControlled = controlledValue !== undefined;\n const currentMode = isControlled ? controlledValue : contextMode;\n\n const handleModeChange = (newMode: 'light' | 'dark') => {\n if (isControlled) {\n onValueChange?.(newMode);\n } else {\n setContextMode(newMode);\n }\n };\n\n const groupClasses = [\n styles.toggleGroup,\n styles[`size${size.charAt(0).toUpperCase() + size.slice(1)}`],\n className,\n ].filter(Boolean).join(' ');\n\n const getButtonClasses = (buttonMode: ThemeMode) => {\n return [\n styles.toggleButton,\n currentMode === buttonMode && styles.toggleButtonActive,\n ].filter(Boolean).join(' ');\n };\n\n const label = ariaLabel || 'Theme toggle';\n\n return (\n <div\n className={groupClasses}\n role=\"group\"\n aria-label={label}\n >\n <button\n type=\"button\"\n className={getButtonClasses('light')}\n onClick={() => handleModeChange('light')}\n aria-pressed={currentMode === 'light'}\n aria-label=\"Light mode\"\n >\n <SunIcon />\n </button>\n <button\n type=\"button\"\n className={getButtonClasses('dark')}\n onClick={() => handleModeChange('dark')}\n aria-pressed={currentMode === 'dark'}\n aria-label=\"Dark mode\"\n >\n <MoonIcon />\n </button>\n {showSystem && !isControlled && (\n <button\n type=\"button\"\n className={getButtonClasses('system')}\n onClick={() => setContextMode('system')}\n aria-pressed={contextMode === 'system'}\n aria-label=\"System preference\"\n >\n <MonitorIcon />\n </button>\n )}\n </div>\n );\n}\n\n// ============================================\n// Exports\n// ============================================\n\nexport const Theme = Object.assign(ThemeProvider, {\n Root: ThemeProvider,\n Provider: ThemeProvider,\n Toggle: ThemeToggle,\n useTheme,\n});\n\nexport { ThemeProvider, ThemeToggle, useTheme };\n\n// ============================================\n// configureTheme — JS-only seed configuration\n// ============================================\n\nexport type NeutralPalette = 'stone' | 'ice' | 'earth' | 'sand' | 'fire';\nexport type DensityPreset = 'compact' | 'default' | 'relaxed';\nexport type RadiusStyle = 'sharp' | 'subtle' | 'default' | 'rounded' | 'pill';\n\nexport interface ConfigureThemeOptions {\n /** Brand/accent color as hex */\n brand?: string;\n /** Neutral palette name */\n neutral?: NeutralPalette;\n /** Spacing density preset */\n density?: DensityPreset;\n /** Border radius style */\n radiusStyle?: RadiusStyle;\n /** Danger/error color as hex */\n danger?: string;\n /** Success color as hex */\n success?: string;\n /** Warning color as hex */\n warning?: string;\n /** Info color as hex */\n info?: string;\n}\n\n// -- Radius presets (match _radius.scss) --\n\nconst RADIUS_PRESETS: Record<RadiusStyle, Record<string, string>> = {\n sharp: { sm: '0', md: '0', lg: '0', xl: '0' },\n subtle: { sm: '0.125rem', md: '0.25rem', lg: '0.375rem', xl: '0.5rem' },\n default: { sm: '0.25rem', md: '0.429rem', lg: '0.571rem', xl: '0.857rem' },\n rounded: { sm: '0.375rem', md: '0.5rem', lg: '0.75rem', xl: '1rem' },\n pill: { sm: '0.5rem', md: '0.75rem', lg: '1rem', xl: '1.5rem' },\n};\n\n// -- Density presets (match _density.scss) --\n\ninterface DensityConfig {\n baseUnit: number;\n baseFontSize: number;\n buttonHeights: [number, number, number]; // sm, md, lg\n inputHeights: [number, number, number]; // sm, default, lg\n}\n\nconst DENSITY_CONFIGS: Record<DensityPreset, DensityConfig> = {\n compact: { baseUnit: 6, baseFontSize: 14, buttonHeights: [24, 30, 36], inputHeights: [24, 32, 36] },\n default: { baseUnit: 7, baseFontSize: 14, buttonHeights: [28, 36, 44], inputHeights: [28, 40, 44] },\n relaxed: { baseUnit: 8, baseFontSize: 14, buttonHeights: [32, 40, 48], inputHeights: [32, 44, 48] },\n};\n\nfunction pxToRem(px: number, baseFontSize: number): string {\n return `${px / baseFontSize}rem`;\n}\n\nfunction hexToRgb(hex: string): [number, number, number] | null {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (!result) return null;\n return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];\n}\n\nfunction adjustLightness(hex: string, amount: number): string {\n const rgb = hexToRgb(hex);\n if (!rgb) return hex;\n const [r, g, b] = rgb;\n const adjust = (v: number) => Math.max(0, Math.min(255, Math.round(v + amount)));\n return `#${adjust(r).toString(16).padStart(2, '0')}${adjust(g).toString(16).padStart(2, '0')}${adjust(b).toString(16).padStart(2, '0')}`;\n}\n\nfunction setVar(el: HTMLElement, name: string, value: string) {\n el.style.setProperty(name, value);\n}\n\n/**\n * Configure theme seeds at runtime via JS. Sets CSS custom properties on\n * `:root` without requiring SCSS. Call this once at app startup.\n *\n * Note: For full control over all 120+ tokens, use the SCSS `@use...with()`\n * approach. `configureTheme` covers the most commonly customized tokens.\n *\n * @example\n * ```ts\n * import { configureTheme } from '@fragments-sdk/ui';\n *\n * configureTheme({\n * brand: '#6366f1',\n * neutral: 'ice',\n * density: 'compact',\n * radiusStyle: 'rounded',\n * });\n * ```\n */\nexport function configureTheme(options: ConfigureThemeOptions): void {\n if (typeof document === 'undefined') return;\n\n const root = document.documentElement;\n\n // -- Brand / Accent --\n if (options.brand) {\n setVar(root, '--fui-color-accent', options.brand);\n setVar(root, '--fui-color-accent-hover', adjustLightness(options.brand, -20));\n setVar(root, '--fui-color-accent-active', adjustLightness(options.brand, -40));\n setVar(root, '--fui-focus-ring-color', `${options.brand}66`); // 40% alpha\n }\n\n // -- Semantic colors --\n if (options.danger) {\n setVar(root, '--fui-color-danger', options.danger);\n setVar(root, '--fui-color-danger-hover', adjustLightness(options.danger, -20));\n }\n if (options.success) setVar(root, '--fui-color-success', options.success);\n if (options.warning) setVar(root, '--fui-color-warning', options.warning);\n if (options.info) setVar(root, '--fui-color-info', options.info);\n\n // -- Radius --\n if (options.radiusStyle) {\n const r = RADIUS_PRESETS[options.radiusStyle];\n if (r) {\n setVar(root, '--fui-radius-sm', r.sm);\n setVar(root, '--fui-radius-md', r.md);\n setVar(root, '--fui-radius-lg', r.lg);\n setVar(root, '--fui-radius-xl', r.xl);\n }\n }\n\n // -- Density --\n if (options.density) {\n const d = DENSITY_CONFIGS[options.density];\n if (d) {\n const unitRem = d.baseUnit / d.baseFontSize;\n\n // Spacing scale\n setVar(root, '--fui-space-1', `${unitRem}rem`);\n setVar(root, '--fui-space-2', `${unitRem * 2}rem`);\n setVar(root, '--fui-space-3', `${unitRem * 3}rem`);\n setVar(root, '--fui-space-4', `${unitRem * 4}rem`);\n setVar(root, '--fui-space-5', `${unitRem * 5}rem`);\n setVar(root, '--fui-space-6', `${unitRem * 6}rem`);\n setVar(root, '--fui-space-8', `${unitRem * 8}rem`);\n setVar(root, '--fui-space-10', `${unitRem * 10}rem`);\n setVar(root, '--fui-space-12', `${unitRem * 12}rem`);\n\n // Component heights\n setVar(root, '--fui-button-height-sm', pxToRem(d.buttonHeights[0], d.baseFontSize));\n setVar(root, '--fui-button-height-md', pxToRem(d.buttonHeights[1], d.baseFontSize));\n setVar(root, '--fui-button-height-lg', pxToRem(d.buttonHeights[2], d.baseFontSize));\n setVar(root, '--fui-input-height-sm', pxToRem(d.inputHeights[0], d.baseFontSize));\n setVar(root, '--fui-input-height', pxToRem(d.inputHeights[1], d.baseFontSize));\n setVar(root, '--fui-input-height-lg', pxToRem(d.inputHeights[2], d.baseFontSize));\n\n // Base unit\n setVar(root, '--fui-base-unit', `${d.baseUnit}px`);\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -135,8 +135,10 @@ exports.Sidebar = index$v.Sidebar;
|
|
|
135
135
|
exports.SidebarProvider = index$v.SidebarProvider;
|
|
136
136
|
exports.useSidebar = index$v.useSidebar;
|
|
137
137
|
exports.useSidebarContext = index$v.useSidebarContext;
|
|
138
|
+
exports.Theme = index$w.Theme;
|
|
138
139
|
exports.ThemeProvider = index$w.ThemeProvider;
|
|
139
140
|
exports.ThemeToggle = index$w.ThemeToggle;
|
|
141
|
+
exports.configureTheme = index$w.configureTheme;
|
|
140
142
|
exports.useTheme = index$w.useTheme;
|
|
141
143
|
exports.Header = index$x.Header;
|
|
142
144
|
exports.AppShell = index$y.AppShell;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|