@work-rjkashyap/unified-ui 0.1.2 → 0.2.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.
@@ -2,7 +2,7 @@ import { blue, red, amber, green, zinc, slate, gray, teal, brand, zIndex, shadow
2
2
  import { fontFamily } from './chunk-ITBG42M5.mjs';
3
3
  import { easingCSS, durationCSS } from './chunk-EZ2L3XPS.mjs';
4
4
  import { cn } from './chunk-ZT3PCXDF.mjs';
5
- import { createContext, useContext, useState, useEffect, useMemo, useCallback, useRef } from 'react';
5
+ import { createContext, useContext, useState, useRef, useEffect, useCallback, useMemo } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  // src/theme/contract.ts
@@ -210,96 +210,6 @@ var cssVar = {
210
210
  /** Returns the raw `var(--<key>)` — same as color() since values are complete oklch */
211
211
  colorChannels: (key) => `var(${colorVarNames[key]})`
212
212
  };
213
- var DSThemeContext = createContext(null);
214
- function useDSTheme() {
215
- const ctx = useContext(DSThemeContext);
216
- if (!ctx) {
217
- throw new Error(
218
- "useDSTheme must be used within a <DSThemeProvider>. Wrap your application (or layout) with <DSThemeProvider>."
219
- );
220
- }
221
- return ctx;
222
- }
223
- function getSystemPreference() {
224
- if (typeof window === "undefined") return "light";
225
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
226
- }
227
- function resolveTheme(theme) {
228
- if (theme === "system") return getSystemPreference();
229
- return theme;
230
- }
231
- var STORAGE_KEY = "ds-theme-preference";
232
- function getStoredTheme() {
233
- if (typeof window === "undefined") return "system";
234
- try {
235
- const stored = localStorage.getItem(STORAGE_KEY);
236
- if (stored === "light" || stored === "dark" || stored === "system") {
237
- return stored;
238
- }
239
- } catch {
240
- }
241
- return "system";
242
- }
243
- function storeTheme(theme) {
244
- try {
245
- localStorage.setItem(STORAGE_KEY, theme);
246
- } catch {
247
- }
248
- }
249
- function DSThemeProvider({
250
- children,
251
- defaultTheme,
252
- manageHtmlClass = false
253
- }) {
254
- const [theme, setThemeState] = useState(
255
- () => defaultTheme ?? getStoredTheme()
256
- );
257
- const [systemPreference, setSystemPreference] = useState("light");
258
- useEffect(() => {
259
- setSystemPreference(getSystemPreference());
260
- const mql = window.matchMedia("(prefers-color-scheme: dark)");
261
- const handler = (e) => {
262
- setSystemPreference(e.matches ? "dark" : "light");
263
- };
264
- mql.addEventListener("change", handler);
265
- return () => mql.removeEventListener("change", handler);
266
- }, []);
267
- const resolvedTheme = useMemo(
268
- () => theme === "system" ? systemPreference : theme,
269
- [theme, systemPreference]
270
- );
271
- useEffect(() => {
272
- if (!manageHtmlClass) return;
273
- const root = document.documentElement;
274
- if (resolvedTheme === "dark") {
275
- root.classList.add("dark");
276
- } else {
277
- root.classList.remove("dark");
278
- }
279
- }, [resolvedTheme, manageHtmlClass]);
280
- const setTheme = useCallback((newTheme) => {
281
- setThemeState(newTheme);
282
- storeTheme(newTheme);
283
- }, []);
284
- const toggleTheme = useCallback(() => {
285
- setThemeState((current) => {
286
- const resolved = resolveTheme(current);
287
- const next = resolved === "dark" ? "light" : "dark";
288
- storeTheme(next);
289
- return next;
290
- });
291
- }, []);
292
- const value = useMemo(
293
- () => ({
294
- theme,
295
- resolvedTheme,
296
- setTheme,
297
- toggleTheme
298
- }),
299
- [theme, resolvedTheme, setTheme, toggleTheme]
300
- );
301
- return /* @__PURE__ */ jsx(DSThemeContext.Provider, { value, children });
302
- }
303
213
 
304
214
  // src/theme/presets.ts
305
215
  var STATUS_LIGHT = {
@@ -839,7 +749,119 @@ var SURFACE_STYLE_PRESETS = [
839
749
  }
840
750
  ];
841
751
  var DEFAULT_SURFACE_STYLE_KEY = "bordered";
752
+ var STYLE_PRESETS = [
753
+ {
754
+ name: "Vega",
755
+ key: "vega",
756
+ description: "The classic shadcn/ui look. Clean, neutral, and familiar.",
757
+ iconPath: "M3 3h18v18H3V3zm2 2v14h14V5H7z",
758
+ defaults: {
759
+ radius: "0.625",
760
+ font: "outfit",
761
+ shadow: "default",
762
+ surfaceStyle: "bordered"
763
+ },
764
+ vars: {
765
+ spacingUnit: "1",
766
+ paddingCard: "1.5rem",
767
+ paddingButtonX: "1rem",
768
+ paddingButtonY: "0.5rem",
769
+ gapDefault: "0.75rem",
770
+ borderWidth: "1px",
771
+ controlHeight: "2.25rem"
772
+ }
773
+ },
774
+ {
775
+ name: "Nova",
776
+ key: "nova",
777
+ description: "Reduced padding and margins for compact layouts.",
778
+ iconPath: "M4 4h16v16H4V4zm1.5 1.5v13h13v-13h-13z",
779
+ defaults: {
780
+ radius: "0.5",
781
+ font: "inter",
782
+ shadow: "subtle",
783
+ surfaceStyle: "bordered"
784
+ },
785
+ vars: {
786
+ spacingUnit: "0.875",
787
+ paddingCard: "1rem",
788
+ paddingButtonX: "0.75rem",
789
+ paddingButtonY: "0.375rem",
790
+ gapDefault: "0.5rem",
791
+ borderWidth: "1px",
792
+ controlHeight: "2rem"
793
+ }
794
+ },
795
+ {
796
+ name: "Maia",
797
+ key: "maia",
798
+ description: "Soft and rounded, with generous spacing.",
799
+ iconPath: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z",
800
+ defaults: {
801
+ radius: "0.75",
802
+ font: "outfit",
803
+ shadow: "default",
804
+ surfaceStyle: "mixed"
805
+ },
806
+ vars: {
807
+ spacingUnit: "1.125",
808
+ paddingCard: "1.75rem",
809
+ paddingButtonX: "1.25rem",
810
+ paddingButtonY: "0.625rem",
811
+ gapDefault: "1rem",
812
+ borderWidth: "1px",
813
+ controlHeight: "2.5rem"
814
+ }
815
+ },
816
+ {
817
+ name: "Lyra",
818
+ key: "lyra",
819
+ description: "Boxy and sharp. Pairs well with mono fonts.",
820
+ iconPath: "M3 3h18v18H3V3zm1 1v16h16V4H4z",
821
+ defaults: {
822
+ radius: "0",
823
+ font: "system",
824
+ shadow: "none",
825
+ surfaceStyle: "bordered"
826
+ },
827
+ vars: {
828
+ spacingUnit: "1",
829
+ paddingCard: "1.25rem",
830
+ paddingButtonX: "1rem",
831
+ paddingButtonY: "0.5rem",
832
+ gapDefault: "0.75rem",
833
+ borderWidth: "1px",
834
+ controlHeight: "2.25rem"
835
+ }
836
+ },
837
+ {
838
+ name: "Mira",
839
+ key: "mira",
840
+ description: "Compact. Made for dense interfaces.",
841
+ iconPath: "M5 3h14a2 2 0 012 2v14a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2zm0 2v14h14V5H5zm2 2h10v2H7V7zm0 4h10v2H7v-2zm0 4h7v2H7v-2z",
842
+ defaults: {
843
+ radius: "0.375",
844
+ font: "inter",
845
+ shadow: "none",
846
+ surfaceStyle: "bordered"
847
+ },
848
+ vars: {
849
+ spacingUnit: "0.75",
850
+ paddingCard: "0.75rem",
851
+ paddingButtonX: "0.625rem",
852
+ paddingButtonY: "0.25rem",
853
+ gapDefault: "0.375rem",
854
+ borderWidth: "1px",
855
+ controlHeight: "1.75rem"
856
+ }
857
+ }
858
+ ];
859
+ var DEFAULT_STYLE_KEY = "vega";
860
+ function getStylePreset(key) {
861
+ return STYLE_PRESETS.find((s) => s.key === key) ?? STYLE_PRESETS[0];
862
+ }
842
863
  var DEFAULT_THEME_CONFIG = {
864
+ style: DEFAULT_STYLE_KEY,
843
865
  colorPreset: "zinc",
844
866
  radius: DEFAULT_RADIUS_KEY,
845
867
  font: DEFAULT_FONT_KEY,
@@ -925,7 +947,15 @@ function buildThemeOverrides(config, mode) {
925
947
  }
926
948
  }
927
949
  const radiusPreset = getRadiusPreset(config.radius);
950
+ const baseRem = Number.parseFloat(radiusPreset.value);
951
+ const isZero = radiusPreset.key === "0";
928
952
  vars["--radius"] = radiusPreset.value;
953
+ vars["--radius-none"] = "0px";
954
+ vars["--radius-sm"] = isZero ? "0px" : `${Math.max(baseRem * 0.4, 0.125)}rem`;
955
+ vars["--radius-md"] = isZero ? "0px" : `${Math.max(baseRem * 0.6, 0.25)}rem`;
956
+ vars["--radius-lg"] = isZero ? "0px" : `${Math.max(baseRem * 0.8, 0.375)}rem`;
957
+ vars["--radius-xl"] = isZero ? "0px" : `${Math.max(baseRem * 1.2, 0.5)}rem`;
958
+ vars["--radius-full"] = "9999px";
929
959
  const fontPreset = getFontPreset(config.font);
930
960
  vars["--font-sans"] = fontPreset.value;
931
961
  const shadowPreset = getShadowPreset(config.shadow);
@@ -937,6 +967,23 @@ function buildThemeOverrides(config, mode) {
937
967
  vars["--shadow-lg"] = shadows.lg;
938
968
  vars["--shadow-xl"] = shadows.xl;
939
969
  vars["--shadow-2xl"] = shadows["2xl"];
970
+ const stylePreset = getStylePreset(config.style);
971
+ const sv = stylePreset.vars;
972
+ vars["--ds-spacing-unit"] = sv.spacingUnit;
973
+ vars["--ds-padding-card"] = sv.paddingCard;
974
+ vars["--ds-padding-button-x"] = sv.paddingButtonX;
975
+ vars["--ds-padding-button-y"] = sv.paddingButtonY;
976
+ vars["--ds-gap-default"] = sv.gapDefault;
977
+ vars["--ds-border-width"] = sv.borderWidth;
978
+ vars["--ds-control-height"] = sv.controlHeight;
979
+ if (config.surfaceStyle === "elevated") {
980
+ vars["--card"] = mode === "dark" ? "oklch(0.205 0 0)" : "oklch(1 0 0)";
981
+ vars["--border"] = mode === "dark" ? "oklch(0.205 0 0 / 0)" : "oklch(0.922 0 0 / 0)";
982
+ vars["--border-muted"] = mode === "dark" ? "oklch(0.205 0 0 / 0)" : "oklch(0.922 0 0 / 0)";
983
+ } else if (config.surfaceStyle === "mixed") {
984
+ vars["--border"] = mode === "dark" ? "oklch(0.4 0 0 / 0.15)" : "oklch(0.8 0 0 / 0.3)";
985
+ vars["--border-muted"] = mode === "dark" ? "oklch(0.4 0 0 / 0.1)" : "oklch(0.85 0 0 / 0.25)";
986
+ }
940
987
  return vars;
941
988
  }
942
989
  function generateThemeCSS(config) {
@@ -947,6 +994,7 @@ function generateThemeCSS(config) {
947
994
  "/* ============================================",
948
995
  " * Unified UI \u2014 Custom Theme",
949
996
  ` * Preset: ${getColorPreset(config.colorPreset).name}`,
997
+ ` * Style: ${getStylePreset(config.style).name}`,
950
998
  ` * Radius: ${getRadiusPreset(config.radius).label}`,
951
999
  ` * Font: ${getFontPreset(config.font).name}`,
952
1000
  ` * Shadows: ${getShadowPreset(config.shadow).name}`,
@@ -961,7 +1009,7 @@ function generateThemeCSS(config) {
961
1009
  "}"
962
1010
  ].join("\n");
963
1011
  }
964
- var STORAGE_KEY2 = "ds-theme-customizer";
1012
+ var STORAGE_KEY = "ds-theme-customizer";
965
1013
  var STYLE_ELEMENT_ID = "ds-theme-customizer";
966
1014
  var ThemeCustomizerContext = createContext(null);
967
1015
  function useThemeCustomizer() {
@@ -976,10 +1024,11 @@ function useThemeCustomizer() {
976
1024
  function loadConfig() {
977
1025
  if (typeof window === "undefined") return DEFAULT_THEME_CONFIG;
978
1026
  try {
979
- const raw = localStorage.getItem(STORAGE_KEY2);
1027
+ const raw = localStorage.getItem(STORAGE_KEY);
980
1028
  if (!raw) return DEFAULT_THEME_CONFIG;
981
1029
  const parsed = JSON.parse(raw);
982
1030
  return {
1031
+ style: STYLE_PRESETS.some((s) => s.key === parsed.style) ? parsed.style : DEFAULT_THEME_CONFIG.style,
983
1032
  colorPreset: COLOR_PRESET_KEYS.includes(parsed.colorPreset ?? "") ? parsed.colorPreset : DEFAULT_THEME_CONFIG.colorPreset,
984
1033
  radius: RADIUS_PRESETS.some((r) => r.key === parsed.radius) ? parsed.radius : DEFAULT_THEME_CONFIG.radius,
985
1034
  font: FONT_PRESETS.some((f) => f.key === parsed.font) ? parsed.font : DEFAULT_THEME_CONFIG.font,
@@ -995,7 +1044,7 @@ function loadConfig() {
995
1044
  function saveConfig(config) {
996
1045
  if (typeof window === "undefined") return;
997
1046
  try {
998
- localStorage.setItem(STORAGE_KEY2, JSON.stringify(config));
1047
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(config));
999
1048
  } catch {
1000
1049
  }
1001
1050
  }
@@ -1005,7 +1054,9 @@ function getResolvedMode() {
1005
1054
  }
1006
1055
  function injectStyles(config) {
1007
1056
  if (typeof document === "undefined") return;
1008
- let styleEl = document.getElementById(STYLE_ELEMENT_ID);
1057
+ let styleEl = document.getElementById(
1058
+ STYLE_ELEMENT_ID
1059
+ );
1009
1060
  if (!styleEl) {
1010
1061
  styleEl = document.createElement("style");
1011
1062
  styleEl.id = STYLE_ELEMENT_ID;
@@ -1028,7 +1079,7 @@ function removeStyles() {
1028
1079
  }
1029
1080
  }
1030
1081
  function configsEqual(a, b) {
1031
- return a.colorPreset === b.colorPreset && a.radius === b.radius && a.font === b.font && a.shadow === b.shadow && a.surfaceStyle === b.surfaceStyle;
1082
+ return a.style === b.style && a.colorPreset === b.colorPreset && a.radius === b.radius && a.font === b.font && a.shadow === b.shadow && a.surfaceStyle === b.surfaceStyle;
1032
1083
  }
1033
1084
  function ThemeCustomizerProvider({
1034
1085
  children,
@@ -1038,7 +1089,7 @@ function ThemeCustomizerProvider({
1038
1089
  const [config, setConfigState] = useState(
1039
1090
  () => defaultConfig ?? loadConfig()
1040
1091
  );
1041
- const [resolvedMode, setResolvedMode] = useState("light");
1092
+ const [_resolvedMode, setResolvedMode] = useState("light");
1042
1093
  const configRef = useRef(config);
1043
1094
  configRef.current = config;
1044
1095
  useEffect(() => {
@@ -1060,7 +1111,7 @@ function ThemeCustomizerProvider({
1060
1111
  if (applyStyles) {
1061
1112
  injectStyles(config);
1062
1113
  }
1063
- }, [config, resolvedMode, applyStyles]);
1114
+ }, [config, applyStyles]);
1064
1115
  useEffect(() => {
1065
1116
  saveConfig(config);
1066
1117
  }, [config]);
@@ -1073,7 +1124,7 @@ function ThemeCustomizerProvider({
1073
1124
  }, [applyStyles]);
1074
1125
  useEffect(() => {
1075
1126
  const handleStorage = (e) => {
1076
- if (e.key === STORAGE_KEY2 && e.newValue) {
1127
+ if (e.key === STORAGE_KEY && e.newValue) {
1077
1128
  try {
1078
1129
  const parsed = JSON.parse(e.newValue);
1079
1130
  setConfigState((prev) => {
@@ -1090,6 +1141,20 @@ function ThemeCustomizerProvider({
1090
1141
  const setConfig = useCallback((newConfig) => {
1091
1142
  setConfigState(newConfig);
1092
1143
  }, []);
1144
+ const setStyle = useCallback((key) => {
1145
+ const preset = getStylePreset(key);
1146
+ setConfigState((prev) => {
1147
+ if (prev.style === key) return prev;
1148
+ return {
1149
+ ...prev,
1150
+ style: key,
1151
+ radius: preset.defaults.radius,
1152
+ font: preset.defaults.font,
1153
+ shadow: preset.defaults.shadow,
1154
+ surfaceStyle: preset.defaults.surfaceStyle
1155
+ };
1156
+ });
1157
+ }, []);
1093
1158
  const setColorPreset = useCallback((key) => {
1094
1159
  setConfigState((prev) => {
1095
1160
  if (prev.colorPreset === key) return prev;
@@ -1131,6 +1196,7 @@ function ThemeCustomizerProvider({
1131
1196
  () => ({
1132
1197
  config,
1133
1198
  setConfig,
1199
+ setStyle,
1134
1200
  setColorPreset,
1135
1201
  setRadius,
1136
1202
  setFont,
@@ -1143,6 +1209,7 @@ function ThemeCustomizerProvider({
1143
1209
  [
1144
1210
  config,
1145
1211
  setConfig,
1212
+ setStyle,
1146
1213
  setColorPreset,
1147
1214
  setRadius,
1148
1215
  setFont,
@@ -1242,7 +1309,7 @@ function RadiusOption({
1242
1309
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1243
1310
  isActive ? "border-primary bg-muted/60 shadow-sm" : "border-border bg-transparent"
1244
1311
  ),
1245
- title: preset.name + " (" + preset.label + ")",
1312
+ title: `${preset.name} (${preset.label})`,
1246
1313
  children: [
1247
1314
  /* @__PURE__ */ jsx(
1248
1315
  "span",
@@ -1332,6 +1399,69 @@ function PillToggle({
1332
1399
  }
1333
1400
  );
1334
1401
  }
1402
+ function StyleOption({
1403
+ preset,
1404
+ isActive,
1405
+ onClick
1406
+ }) {
1407
+ return /* @__PURE__ */ jsxs(
1408
+ "button",
1409
+ {
1410
+ type: "button",
1411
+ onClick,
1412
+ className: cn(
1413
+ "flex items-start gap-3 rounded-md border px-3 py-3 text-left transition-all duration-fast ease-standard",
1414
+ "hover:border-border-strong hover:bg-muted/50",
1415
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1416
+ isActive ? "border-primary bg-muted/60 shadow-sm" : "border-border bg-transparent"
1417
+ ),
1418
+ title: preset.description,
1419
+ children: [
1420
+ /* @__PURE__ */ jsx(
1421
+ "svg",
1422
+ {
1423
+ className: cn(
1424
+ "size-5 shrink-0 mt-0.5",
1425
+ isActive ? "text-primary" : "text-muted-foreground"
1426
+ ),
1427
+ xmlns: "http://www.w3.org/2000/svg",
1428
+ viewBox: "0 0 24 24",
1429
+ fill: "none",
1430
+ stroke: "currentColor",
1431
+ strokeWidth: "1.5",
1432
+ strokeLinecap: "round",
1433
+ strokeLinejoin: "round",
1434
+ "aria-hidden": "true",
1435
+ children: /* @__PURE__ */ jsx("path", { d: preset.iconPath })
1436
+ }
1437
+ ),
1438
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1439
+ /* @__PURE__ */ jsx(
1440
+ "div",
1441
+ {
1442
+ className: cn(
1443
+ "text-sm font-semibold leading-tight",
1444
+ isActive ? "text-foreground" : "text-foreground"
1445
+ ),
1446
+ children: preset.name
1447
+ }
1448
+ ),
1449
+ /* @__PURE__ */ jsx(
1450
+ "div",
1451
+ {
1452
+ className: cn(
1453
+ "mt-0.5 text-xs leading-snug",
1454
+ isActive ? "text-muted-foreground" : "text-muted-foreground/70"
1455
+ ),
1456
+ children: preset.description
1457
+ }
1458
+ )
1459
+ ] }),
1460
+ isActive && /* @__PURE__ */ jsx(CheckIcon, { className: "shrink-0 mt-0.5 text-primary" })
1461
+ ]
1462
+ }
1463
+ );
1464
+ }
1335
1465
  function CopyButton({
1336
1466
  getText,
1337
1467
  className,
@@ -1397,6 +1527,7 @@ function ThemeCustomizer({
1397
1527
  }) {
1398
1528
  const {
1399
1529
  config,
1530
+ setStyle,
1400
1531
  setColorPreset,
1401
1532
  setRadius,
1402
1533
  setFont,
@@ -1413,6 +1544,15 @@ function ThemeCustomizer({
1413
1544
  "data-ds": "",
1414
1545
  "data-ds-component": "theme-customizer",
1415
1546
  children: [
1547
+ /* @__PURE__ */ jsx(Section, { title: "Style", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-2", children: STYLE_PRESETS.map((preset) => /* @__PURE__ */ jsx(
1548
+ StyleOption,
1549
+ {
1550
+ preset,
1551
+ isActive: config.style === preset.key,
1552
+ onClick: () => setStyle(preset.key)
1553
+ },
1554
+ preset.key
1555
+ )) }) }),
1416
1556
  /* @__PURE__ */ jsx(Section, { title: "Color", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: COLOR_PRESETS.map((preset) => /* @__PURE__ */ jsx(
1417
1557
  ColorSwatch,
1418
1558
  {
@@ -1501,5 +1641,95 @@ function ThemeCustomizer({
1501
1641
  );
1502
1642
  }
1503
1643
  ThemeCustomizer.displayName = "ThemeCustomizer";
1644
+ var DSThemeContext = createContext(null);
1645
+ function useDSTheme() {
1646
+ const ctx = useContext(DSThemeContext);
1647
+ if (!ctx) {
1648
+ throw new Error(
1649
+ "useDSTheme must be used within a <DSThemeProvider>. Wrap your application (or layout) with <DSThemeProvider>."
1650
+ );
1651
+ }
1652
+ return ctx;
1653
+ }
1654
+ function getSystemPreference() {
1655
+ if (typeof window === "undefined") return "light";
1656
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1657
+ }
1658
+ function resolveTheme(theme) {
1659
+ if (theme === "system") return getSystemPreference();
1660
+ return theme;
1661
+ }
1662
+ var STORAGE_KEY2 = "ds-theme-preference";
1663
+ function getStoredTheme() {
1664
+ if (typeof window === "undefined") return "system";
1665
+ try {
1666
+ const stored = localStorage.getItem(STORAGE_KEY2);
1667
+ if (stored === "light" || stored === "dark" || stored === "system") {
1668
+ return stored;
1669
+ }
1670
+ } catch {
1671
+ }
1672
+ return "system";
1673
+ }
1674
+ function storeTheme(theme) {
1675
+ try {
1676
+ localStorage.setItem(STORAGE_KEY2, theme);
1677
+ } catch {
1678
+ }
1679
+ }
1680
+ function DSThemeProvider({
1681
+ children,
1682
+ defaultTheme,
1683
+ manageHtmlClass = false
1684
+ }) {
1685
+ const [theme, setThemeState] = useState(
1686
+ () => defaultTheme ?? getStoredTheme()
1687
+ );
1688
+ const [systemPreference, setSystemPreference] = useState("light");
1689
+ useEffect(() => {
1690
+ setSystemPreference(getSystemPreference());
1691
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
1692
+ const handler = (e) => {
1693
+ setSystemPreference(e.matches ? "dark" : "light");
1694
+ };
1695
+ mql.addEventListener("change", handler);
1696
+ return () => mql.removeEventListener("change", handler);
1697
+ }, []);
1698
+ const resolvedTheme = useMemo(
1699
+ () => theme === "system" ? systemPreference : theme,
1700
+ [theme, systemPreference]
1701
+ );
1702
+ useEffect(() => {
1703
+ if (!manageHtmlClass) return;
1704
+ const root = document.documentElement;
1705
+ if (resolvedTheme === "dark") {
1706
+ root.classList.add("dark");
1707
+ } else {
1708
+ root.classList.remove("dark");
1709
+ }
1710
+ }, [resolvedTheme, manageHtmlClass]);
1711
+ const setTheme = useCallback((newTheme) => {
1712
+ setThemeState(newTheme);
1713
+ storeTheme(newTheme);
1714
+ }, []);
1715
+ const toggleTheme = useCallback(() => {
1716
+ setThemeState((current) => {
1717
+ const resolved = resolveTheme(current);
1718
+ const next = resolved === "dark" ? "light" : "dark";
1719
+ storeTheme(next);
1720
+ return next;
1721
+ });
1722
+ }, []);
1723
+ const value = useMemo(
1724
+ () => ({
1725
+ theme,
1726
+ resolvedTheme,
1727
+ setTheme,
1728
+ toggleTheme
1729
+ }),
1730
+ [theme, resolvedTheme, setTheme, toggleTheme]
1731
+ );
1732
+ return /* @__PURE__ */ jsx(DSThemeContext.Provider, { value, children });
1733
+ }
1504
1734
 
1505
- export { COLOR_PRESETS, COLOR_PRESET_KEYS, DEFAULT_FONT_KEY, DEFAULT_RADIUS_KEY, DEFAULT_SHADOW_KEY, DEFAULT_SURFACE_STYLE_KEY, DEFAULT_THEME_CONFIG, DSThemeProvider, FONT_PRESETS, RADIUS_PRESETS, SHADOW_PRESETS, SURFACE_STYLE_PRESETS, ThemeCustomizer, ThemeCustomizerProvider, buildDarkThemeVars, buildLightThemeVars, buildThemeCSS, buildThemeOverrides, contract, cssVar, generateThemeCSS, getColorPreset, getFontPreset, getRadiusPreset, getShadowPreset, useDSTheme, useThemeCustomizer };
1735
+ export { COLOR_PRESETS, COLOR_PRESET_KEYS, DEFAULT_FONT_KEY, DEFAULT_RADIUS_KEY, DEFAULT_SHADOW_KEY, DEFAULT_STYLE_KEY, DEFAULT_SURFACE_STYLE_KEY, DEFAULT_THEME_CONFIG, DSThemeProvider, FONT_PRESETS, RADIUS_PRESETS, SHADOW_PRESETS, STYLE_PRESETS, SURFACE_STYLE_PRESETS, ThemeCustomizer, ThemeCustomizerProvider, buildDarkThemeVars, buildLightThemeVars, buildThemeCSS, buildThemeOverrides, contract, cssVar, generateThemeCSS, getColorPreset, getFontPreset, getRadiusPreset, getShadowPreset, getStylePreset, useDSTheme, useThemeCustomizer };