@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 CHANGED
@@ -16,12 +16,19 @@ npm install react react-dom
16
16
 
17
17
  ## Setup
18
18
 
19
- Import the global styles in your app entry point:
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-10 seeds and everything derives automatically. Set seed variables in your SCSS before importing:
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 - just your brand color
125
- $fui-brand: #0066ff;
126
- @use '@fragments-sdk/ui/globals';
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
- $fui-brand: #0066ff; // Primary brand color
132
- $fui-neutral: "steel"; // Palette: "steel" | "sand" | "smoke" | "ash" | "silver"
133
- $fui-density: "compact"; // Spacing: "compact" | "default" | "relaxed"
134
- $fui-radius-style: "rounded"; // Corners: "sharp" | "subtle" | "default" | "rounded" | "pill"
135
-
136
- // Optional semantic color overrides
137
- $fui-danger: #dc2626;
138
- $fui-success: #16a34a;
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 | `"ash"` | Neutral palette for surfaces, text, borders |
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
- | `steel` | Cool blue-tinted grays (professional, tech) |
161
- | `sand` | Warm brown-tinted grays (organic, approachable) |
162
- | `smoke` | Pure true grays (minimal, neutral) |
163
- | `ash` | Muted cool neutrals (subtle, balanced) - default |
164
- | `silver` | Bright clean grays (light, modern) |
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/tokens' as *;
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
- $fui-brand: #0066ff;
242
- $fui-neutral: "steel";
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":";;;;;;;;;;;;;;;;;;;;;;AAmEA,MAAM,eAAeA,iBAAM,cAAwC,IAAI;AASvE,SAAS,sBAAwC;AAC/C,QAAM,CAAC,YAAY,aAAa,IAAIA,iBAAM,SAA2B,OAAO;AAE5EA,mBAAM,UAAU,MAAM;AAEpB,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,KAAK,OAAO,WAAW,8BAA8B;AAC3D,kBAAc,GAAG,UAAU,SAAS,OAAO;AAE3C,UAAM,UAAU,CAAC,MAA2B;AAC1C,oBAAc,EAAE,UAAU,SAAS,OAAO;AAAA,IAC5C;AAEA,OAAG,iBAAiB,UAAU,OAAO;AACrC,WAAO,MAAM,GAAG,oBAAoB,UAAU,OAAO;AAAA,EACvD,GAAG,CAAA,CAAE;AAEL,SAAO;AACT;AAKA,SAAS,WAA2B;AAClC,QAAM,UAAUA,iBAAM,WAAW,YAAY;AAE7C,MAAI,CAAC,SAAS;AAEZ,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,MAAC;AAAA,MAChB,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,YAAY,MAAM;AAAA,MAAC;AAAA,IAAA;AAAA,EAEvB;AAEA,SAAO;AACT;AAMA,SAAS,QAAQ,EAAE,OAAO,MAAyB;AACjD,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MAEZ,UAAAA,2BAAAA,IAAC,QAAA,EAAK,GAAE,4kBAAA,CAA4kB;AAAA,IAAA;AAAA,EAAA;AAG1lB;AAEA,SAAS,SAAS,EAAE,OAAO,MAAyB;AAClD,SACEA,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MAEZ,UAAAA,2BAAAA,IAAC,QAAA,EAAK,GAAE,kXAAA,CAAkX;AAAA,IAAA;AAAA,EAAA;AAGhY;AAEA,SAAS,YAAY,EAAE,OAAO,MAAyB;AACrD,SACEA,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,eAAY;AAAA,MAEZ,UAAAA,2BAAAA,IAAC,QAAA,EAAK,GAAE,wOAAA,CAAwO;AAAA,IAAA;AAAA,EAAA;AAGtP;AAUA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA,aAAa;AAAA,EACb,YAAY;AACd,GAAuB;AACrB,QAAM,mBAAmB,oBAAA;AAGzB,QAAM,kBAAkB,eAAe,gBAAgB;AAGvD,QAAM,CAAC,cAAc,eAAe,IAAID,iBAAM,SAAoB,eAAe;AACjF,QAAM,CAAC,SAAS,UAAU,IAAIA,iBAAM,SAAS,KAAK;AAGlD,QAAM,eAAe,mBAAmB;AACxC,QAAM,OAAO,eAAe,iBAAiB;AAG7C,QAAM,eAAiC,SAAS,WAAW,mBAAmB;AAG9EA,mBAAM,UAAU,MAAM;AACpB,QAAI,OAAO,WAAW,YAAa;AAEnC,QAAI,CAAC,gBAAgB,YAAY;AAC/B,YAAM,SAAS,aAAa,QAAQ,UAAU;AAC9C,UAAI,UAAU,CAAC,SAAS,QAAQ,QAAQ,EAAE,SAAS,MAAM,GAAG;AAC1D,wBAAgB,MAAM;AAAA,MACxB;AAAA,IACF;AACA,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,cAAc,UAAU,CAAC;AAI7BA,mBAAM,UAAU,MAAM;AACpB,QAAI,OAAO,aAAa,eAAe,CAAC,QAAS;AAEjD,UAAM,OAAO,SAAS;AAEtB,QAAI,cAAc,cAAc;AAC9B,WAAK,aAAa,cAAc,YAAY;AAAA,IAC9C,WAAW,cAAc,SAAS;AAChC,WAAK,UAAU,OAAO,SAAS,MAAM;AACrC,WAAK,UAAU,IAAI,YAAY;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,cAAc,WAAW,OAAO,CAAC;AAGrCA,mBAAM,UAAU,MAAM;AACpB,QAAI,OAAO,WAAW,eAAe,CAAC,cAAc,CAAC,QAAS;AAC9D,iBAAa,QAAQ,YAAY,IAAI;AAAA,EACvC,GAAG,CAAC,MAAM,YAAY,OAAO,CAAC;AAE9B,QAAM,UAAUA,iBAAM,YAAY,CAAC,YAAuB;AACxD,QAAI,CAAC,cAAc;AACjB,sBAAgB,OAAO;AAAA,IACzB;AACA,iDAAe;AAAA,EACjB,GAAG,CAAC,cAAc,YAAY,CAAC;AAE/B,QAAM,aAAaA,iBAAM,YAAY,MAAM;AACzC,UAAM,OAAO,iBAAiB,UAAU,SAAS;AACjD,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,cAAc,OAAO,CAAC;AAE1B,QAAM,eAAkC;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAGF,wCACG,aAAa,UAAb,EAAsB,OAAO,cAC3B,UACH;AAEJ;AASA,SAAS,YAAY;AAAA,EACnB,OAAO;AAAA,EACP,aAAa;AAAA,EACb,OAAO;AAAA,EACP;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAAqB;AACnB,QAAM,EAAE,MAAM,aAAa,SAAS,eAAA,IAAmB,SAAA;AAGvD,QAAM,eAAe,oBAAoB;AACzC,QAAM,cAAc,eAAe,kBAAkB;AAErD,QAAM,mBAAmB,CAAC,YAA8B;AACtD,QAAI,cAAc;AAChB,qDAAgB;AAAA,IAClB,OAAO;AACL,qBAAe,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,eAAe;AAAA,IACnBE,mBAAAA,QAAO;AAAA,IACPA,mBAAAA,QAAO,OAAO,KAAK,OAAO,CAAC,EAAE,YAAA,IAAgB,KAAK,MAAM,CAAC,CAAC,EAAE;AAAA,IAC5D;AAAA,EAAA,EACA,OAAO,OAAO,EAAE,KAAK,GAAG;AAE1B,QAAM,mBAAmB,CAAC,eAA0B;AAClD,WAAO;AAAA,MACLA,mBAAAA,QAAO;AAAA,MACP,gBAAgB,cAAcA,2BAAO;AAAA,IAAA,EACrC,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,EAC5B;AAEA,QAAM,QAAQ,aAAa;AAE3B,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,MACX,MAAK;AAAA,MACL,cAAY;AAAA,MAEZ,UAAA;AAAA,QAAAF,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAW,iBAAiB,OAAO;AAAA,YACnC,SAAS,MAAM,iBAAiB,OAAO;AAAA,YACvC,gBAAc,gBAAgB;AAAA,YAC9B,cAAW;AAAA,YAEX,yCAAC,SAAA,CAAA,CAAQ;AAAA,UAAA;AAAA,QAAA;AAAA,QAEXA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAW,iBAAiB,MAAM;AAAA,YAClC,SAAS,MAAM,iBAAiB,MAAM;AAAA,YACtC,gBAAc,gBAAgB;AAAA,YAC9B,cAAW;AAAA,YAEX,yCAAC,UAAA,CAAA,CAAS;AAAA,UAAA;AAAA,QAAA;AAAA,QAEX,cAAc,CAAC,gBACdA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAW,iBAAiB,QAAQ;AAAA,YACpC,SAAS,MAAM,eAAe,QAAQ;AAAA,YACtC,gBAAc,gBAAgB;AAAA,YAC9B,cAAW;AAAA,YAEX,yCAAC,aAAA,CAAA,CAAY;AAAA,UAAA;AAAA,QAAA;AAAA,MACf;AAAA,IAAA;AAAA,EAAA;AAIR;AAMqB,OAAO,OAAO,eAAe;AAAA,EAChD,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR;AACF,CAAC;;;;"}
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
- /** Alias for defaultMode (more intuitive naming) */
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,oDAAoD;IACpD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,SAAS,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,yBAAyB;IACzB,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,+DAA+D;IAC/D,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,gBAAgB,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,mDAAmD;IACnD,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iEAAiE;IACjE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,yDAAyD;IACzD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAClD,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4CD;;GAEG;AACH,iBAAS,QAAQ,IAAI,cAAc,CAelC;AAuDD;;;GAGG;AACH,iBAAS,aAAa,CAAC,EACrB,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,IAAI,EAAE,cAAc,EACpB,YAAY,EACZ,UAAwB,EACxB,SAAwB,GACzB,EAAE,kBAAkB,2CA4EpB;AAED;;;;;;GAMG;AACH,iBAAS,WAAW,CAAC,EACnB,IAAW,EACX,UAAkB,EAClB,KAAK,EAAE,eAAe,EACtB,aAAa,EACb,YAAY,EAAE,SAAS,EACvB,SAAS,GACV,EAAE,gBAAgB,2CAmElB;AAMD,eAAO,MAAM,KAAK;;;;;CAKhB,CAAC;AAEH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC"}
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;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}