@q9labsai/chalk-ui 4.1.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  Input: () => Input,
37
37
  Menu: () => Menu,
38
38
  MenuContent: () => MenuContent,
39
+ MenuGroup: () => MenuGroup,
39
40
  MenuGroupLabel: () => MenuGroupLabel,
40
41
  MenuItem: () => MenuItem,
41
42
  MenuRadioGroup: () => MenuRadioGroup,
@@ -286,6 +287,9 @@ function Input({ className, type, label, error, icon, iconPosition = "left", siz
286
287
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
287
288
  import { cva as cva3 } from "class-variance-authority";
288
289
 
290
+ // src/lib/use-portal-theme.ts
291
+ import { useSyncExternalStore } from "react";
292
+
289
293
  // src/lib/theme.ts
290
294
  var THEME_ATTRIBUTE_NAMES = ["data-chalk-theme", "data-theme"];
291
295
  var THEME_CLASS_NAMES = ["dark", "light"];
@@ -325,6 +329,28 @@ var resolvePortalThemeFromDocument = ({ defaultTheme = "light", allowSystem = tr
325
329
  return resolveThemeFromDocument({ defaultTheme, allowSystem });
326
330
  };
327
331
 
332
+ // src/lib/use-portal-theme.ts
333
+ var SYSTEM_DARK_QUERY2 = "(prefers-color-scheme: dark)";
334
+ var WATCHED_ATTRIBUTES = ["class", "data-chalk-theme", "data-theme"];
335
+ function subscribe(onChange) {
336
+ if (typeof document === "undefined") return () => {
337
+ };
338
+ const observer = new MutationObserver(onChange);
339
+ const options = { attributes: true, attributeFilter: [...WATCHED_ATTRIBUTES] };
340
+ observer.observe(document.documentElement, options);
341
+ if (document.body) observer.observe(document.body, options);
342
+ const media = typeof window.matchMedia === "function" ? window.matchMedia(SYSTEM_DARK_QUERY2) : null;
343
+ media?.addEventListener("change", onChange);
344
+ return () => {
345
+ observer.disconnect();
346
+ media?.removeEventListener("change", onChange);
347
+ };
348
+ }
349
+ function usePortalTheme(defaultTheme = "light") {
350
+ const read = () => resolvePortalThemeFromDocument({ defaultTheme });
351
+ return useSyncExternalStore(subscribe, read, () => defaultTheme);
352
+ }
353
+
328
354
  // src/menu.tsx
329
355
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
330
356
  function Menu(props) {
@@ -343,7 +369,7 @@ var menuContentVariants = cva3("z-50 min-w-(--anchor-width) origin-(--transform-
343
369
  defaultVariants: { tone: "default" }
344
370
  });
345
371
  function MenuContent({ className, tone, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, children, ...props }) {
346
- const portalTheme = resolvePortalThemeFromDocument();
372
+ const portalTheme = usePortalTheme();
347
373
  return /* @__PURE__ */ jsx5(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx5(MenuPrimitive.Positioner, { className: "z-50", side, sideOffset, align, alignOffset, children: /* @__PURE__ */ jsx5(MenuPrimitive.Popup, { "data-chalk": true, "data-chalk-theme": portalTheme, "data-slot": "menu-content", className: cn("chalk-root", menuContentVariants({ tone }), className), ...props, children }) }) });
348
374
  }
349
375
  var menuItemClassName = "data-highlighted:bg-current/8 relative flex w-full cursor-default items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0";
@@ -359,6 +385,9 @@ function MenuRadioItem({ className, children, ...props }) {
359
385
  /* @__PURE__ */ jsx5(MenuPrimitive.RadioItemIndicator, { className: "ml-auto flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "m5 12.5 4.5 4.5L19 7.5" }) }) })
360
386
  ] });
361
387
  }
388
+ function MenuGroup(props) {
389
+ return /* @__PURE__ */ jsx5(MenuPrimitive.Group, { "data-slot": "menu-group", ...props });
390
+ }
362
391
  function MenuGroupLabel({ className, ...props }) {
363
392
  return /* @__PURE__ */ jsx5(MenuPrimitive.GroupLabel, { "data-slot": "menu-group-label", className: cn("px-2 pt-1.5 pb-1 font-mono text-[10px] tracking-[0.08em] uppercase opacity-60", className), ...props });
364
393
  }
@@ -418,13 +447,13 @@ function matchMobileQuery() {
418
447
  return typeof window.matchMedia === "function" ? window.matchMedia(MOBILE_QUERY) : null;
419
448
  }
420
449
  function useIsMobile() {
421
- const subscribe = React4.useCallback((onChange) => {
450
+ const subscribe2 = React4.useCallback((onChange) => {
422
451
  const query = matchMobileQuery();
423
452
  query?.addEventListener("change", onChange);
424
453
  return () => query?.removeEventListener("change", onChange);
425
454
  }, []);
426
455
  return React4.useSyncExternalStore(
427
- subscribe,
456
+ subscribe2,
428
457
  () => matchMobileQuery()?.matches ?? false,
429
458
  () => false
430
459
  );
@@ -455,7 +484,7 @@ function TooltipTrigger({ ...props }) {
455
484
  return /* @__PURE__ */ jsx7(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
456
485
  }
457
486
  function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
458
- const portalTheme = resolvePortalThemeFromDocument();
487
+ const portalTheme = usePortalTheme();
459
488
  return /* @__PURE__ */ jsx7(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx7(TooltipPrimitive.Positioner, { align, alignOffset, side, sideOffset, className: "z-[9999]", children: /* @__PURE__ */ jsxs5(
460
489
  TooltipPrimitive.Popup,
461
490
  {
@@ -518,8 +547,8 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange, cla
518
547
  }
519
548
  function Sidebar({ side = "left", variant = "sidebar", collapsible = "icon", title = "Navigation", className, children, ...props }) {
520
549
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
550
+ const portalTheme = usePortalTheme();
521
551
  if (isMobile) {
522
- const portalTheme = resolvePortalThemeFromDocument();
523
552
  return /* @__PURE__ */ jsx8(Drawer.Root, { open: openMobile, onOpenChange: setOpenMobile, swipeDirection: side, children: /* @__PURE__ */ jsxs6(Drawer.Portal, { children: [
524
553
  /* @__PURE__ */ jsx8(Drawer.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0" }),
525
554
  /* @__PURE__ */ jsx8(Drawer.Viewport, { className: cn("fixed inset-y-0 z-50 flex", side === "left" ? "left-0" : "right-0"), children: /* @__PURE__ */ jsxs6(
@@ -1031,7 +1060,7 @@ var ToastAction = React13.forwardRef(function ToastAction2({ className, ...props
1031
1060
  });
1032
1061
  function ToastViewport({ className, children, ...props }) {
1033
1062
  const { toasts } = ToastPrimitive.useToastManager();
1034
- const portalTheme = resolvePortalThemeFromDocument();
1063
+ const portalTheme = usePortalTheme();
1035
1064
  return /* @__PURE__ */ jsxs9(ToastPrimitive.Viewport, { "data-chalk": true, "data-chalk-theme": portalTheme, "data-slot": "toast-viewport", className: cn("chalk-ui-root chalk-ui-toast-viewport", className), ...props, children: [
1036
1065
  children,
1037
1066
  toasts.map((toastObject) => {
@@ -1176,6 +1205,7 @@ export {
1176
1205
  Input,
1177
1206
  Menu,
1178
1207
  MenuContent,
1208
+ MenuGroup,
1179
1209
  MenuGroupLabel,
1180
1210
  MenuItem,
1181
1211
  MenuRadioGroup,
@@ -0,0 +1,3 @@
1
+ import { type ThemeMode } from "./theme";
2
+ export declare function usePortalTheme(defaultTheme?: ThemeMode): ThemeMode;
3
+ //# sourceMappingURL=use-portal-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-portal-theme.d.ts","sourceRoot":"","sources":["../../src/lib/use-portal-theme.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AA4BzE,wBAAgB,cAAc,CAAC,YAAY,GAAE,SAAmB,GAAG,SAAS,CAG3E"}
package/dist/menu.d.ts CHANGED
@@ -10,7 +10,9 @@ declare function MenuContent({ className, tone, side, sideOffset, align, alignOf
10
10
  declare function MenuItem({ className, ...props }: MenuPrimitive.Item.Props): import("react/jsx-runtime").JSX.Element;
11
11
  declare function MenuRadioGroup(props: MenuPrimitive.RadioGroup.Props): import("react/jsx-runtime").JSX.Element;
12
12
  declare function MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props): import("react/jsx-runtime").JSX.Element;
13
+ /** Owns the labelled section a `MenuGroupLabel` names; the label throws without it. */
14
+ declare function MenuGroup(props: MenuPrimitive.Group.Props): import("react/jsx-runtime").JSX.Element;
13
15
  declare function MenuGroupLabel({ className, ...props }: MenuPrimitive.GroupLabel.Props): import("react/jsx-runtime").JSX.Element;
14
16
  declare function MenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props): import("react/jsx-runtime").JSX.Element;
15
- export { Menu, MenuContent, MenuGroupLabel, MenuItem, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuTrigger };
17
+ export { Menu, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuTrigger };
16
18
  //# sourceMappingURL=menu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../src/menu.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAKlE,iBAAS,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,2CAE5C;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,2CAEtD;AAED,QAAA,MAAM,mBAAmB;;8EAQvB,CAAC;AAEH,KAAK,gBAAgB,GAAG,aAAa,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC,GAAG,YAAY,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAErL,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAe,EAAE,UAAc,EAAE,KAAe,EAAE,WAAe,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,2CAYhJ;AAKD,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,2CAElE;AAED,iBAAS,cAAc,CAAC,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,2CAE5D;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,2CAWtF;AAED,iBAAS,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,2CAE9E;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,2CAE5E;AAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../src/menu.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAKlE,iBAAS,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,2CAE5C;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,2CAEtD;AAED,QAAA,MAAM,mBAAmB;;8EAQvB,CAAC;AAEH,KAAK,gBAAgB,GAAG,aAAa,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC,GAAG,YAAY,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAErL,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAe,EAAE,UAAc,EAAE,KAAe,EAAE,WAAe,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,2CAYhJ;AAKD,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,2CAElE;AAED,iBAAS,cAAc,CAAC,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,2CAE5D;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,2CAWtF;AAED,uFAAuF;AACvF,iBAAS,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,2CAElD;AAED,iBAAS,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,UAAU,CAAC,KAAK,2CAE9E;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,2CAE5E;AAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC"}
package/dist/menu.js CHANGED
@@ -11,6 +11,9 @@ function cn(...inputs) {
11
11
  return twMerge(clsx(inputs));
12
12
  }
13
13
 
14
+ // src/lib/use-portal-theme.ts
15
+ import { useSyncExternalStore } from "react";
16
+
14
17
  // src/lib/theme.ts
15
18
  var THEME_ATTRIBUTE_NAMES = ["data-chalk-theme", "data-theme"];
16
19
  var THEME_CLASS_NAMES = ["dark", "light"];
@@ -50,6 +53,28 @@ var resolvePortalThemeFromDocument = ({ defaultTheme = "light", allowSystem = tr
50
53
  return resolveThemeFromDocument({ defaultTheme, allowSystem });
51
54
  };
52
55
 
56
+ // src/lib/use-portal-theme.ts
57
+ var SYSTEM_DARK_QUERY2 = "(prefers-color-scheme: dark)";
58
+ var WATCHED_ATTRIBUTES = ["class", "data-chalk-theme", "data-theme"];
59
+ function subscribe(onChange) {
60
+ if (typeof document === "undefined") return () => {
61
+ };
62
+ const observer = new MutationObserver(onChange);
63
+ const options = { attributes: true, attributeFilter: [...WATCHED_ATTRIBUTES] };
64
+ observer.observe(document.documentElement, options);
65
+ if (document.body) observer.observe(document.body, options);
66
+ const media = typeof window.matchMedia === "function" ? window.matchMedia(SYSTEM_DARK_QUERY2) : null;
67
+ media?.addEventListener("change", onChange);
68
+ return () => {
69
+ observer.disconnect();
70
+ media?.removeEventListener("change", onChange);
71
+ };
72
+ }
73
+ function usePortalTheme(defaultTheme = "light") {
74
+ const read = () => resolvePortalThemeFromDocument({ defaultTheme });
75
+ return useSyncExternalStore(subscribe, read, () => defaultTheme);
76
+ }
77
+
53
78
  // src/menu.tsx
54
79
  import { jsx, jsxs } from "react/jsx-runtime";
55
80
  function Menu(props) {
@@ -68,7 +93,7 @@ var menuContentVariants = cva("z-50 min-w-(--anchor-width) origin-(--transform-o
68
93
  defaultVariants: { tone: "default" }
69
94
  });
70
95
  function MenuContent({ className, tone, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, children, ...props }) {
71
- const portalTheme = resolvePortalThemeFromDocument();
96
+ const portalTheme = usePortalTheme();
72
97
  return /* @__PURE__ */ jsx(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(MenuPrimitive.Positioner, { className: "z-50", side, sideOffset, align, alignOffset, children: /* @__PURE__ */ jsx(MenuPrimitive.Popup, { "data-chalk": true, "data-chalk-theme": portalTheme, "data-slot": "menu-content", className: cn("chalk-root", menuContentVariants({ tone }), className), ...props, children }) }) });
73
98
  }
74
99
  var menuItemClassName = "data-highlighted:bg-current/8 relative flex w-full cursor-default items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0";
@@ -84,6 +109,9 @@ function MenuRadioItem({ className, children, ...props }) {
84
109
  /* @__PURE__ */ jsx(MenuPrimitive.RadioItemIndicator, { className: "ml-auto flex size-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 4.5 4.5L19 7.5" }) }) })
85
110
  ] });
86
111
  }
112
+ function MenuGroup(props) {
113
+ return /* @__PURE__ */ jsx(MenuPrimitive.Group, { "data-slot": "menu-group", ...props });
114
+ }
87
115
  function MenuGroupLabel({ className, ...props }) {
88
116
  return /* @__PURE__ */ jsx(MenuPrimitive.GroupLabel, { "data-slot": "menu-group-label", className: cn("px-2 pt-1.5 pb-1 font-mono text-[10px] tracking-[0.08em] uppercase opacity-60", className), ...props });
89
117
  }
@@ -93,6 +121,7 @@ function MenuSeparator({ className, ...props }) {
93
121
  export {
94
122
  Menu,
95
123
  MenuContent,
124
+ MenuGroup,
96
125
  MenuGroupLabel,
97
126
  MenuItem,
98
127
  MenuRadioGroup,
package/dist/sidebar.js CHANGED
@@ -21,18 +21,21 @@ function matchMobileQuery() {
21
21
  return typeof window.matchMedia === "function" ? window.matchMedia(MOBILE_QUERY) : null;
22
22
  }
23
23
  function useIsMobile() {
24
- const subscribe = React.useCallback((onChange) => {
24
+ const subscribe2 = React.useCallback((onChange) => {
25
25
  const query = matchMobileQuery();
26
26
  query?.addEventListener("change", onChange);
27
27
  return () => query?.removeEventListener("change", onChange);
28
28
  }, []);
29
29
  return React.useSyncExternalStore(
30
- subscribe,
30
+ subscribe2,
31
31
  () => matchMobileQuery()?.matches ?? false,
32
32
  () => false
33
33
  );
34
34
  }
35
35
 
36
+ // src/lib/use-portal-theme.ts
37
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
38
+
36
39
  // src/lib/theme.ts
37
40
  var THEME_ATTRIBUTE_NAMES = ["data-chalk-theme", "data-theme"];
38
41
  var THEME_CLASS_NAMES = ["dark", "light"];
@@ -72,6 +75,28 @@ var resolvePortalThemeFromDocument = ({ defaultTheme = "light", allowSystem = tr
72
75
  return resolveThemeFromDocument({ defaultTheme, allowSystem });
73
76
  };
74
77
 
78
+ // src/lib/use-portal-theme.ts
79
+ var SYSTEM_DARK_QUERY2 = "(prefers-color-scheme: dark)";
80
+ var WATCHED_ATTRIBUTES = ["class", "data-chalk-theme", "data-theme"];
81
+ function subscribe(onChange) {
82
+ if (typeof document === "undefined") return () => {
83
+ };
84
+ const observer = new MutationObserver(onChange);
85
+ const options = { attributes: true, attributeFilter: [...WATCHED_ATTRIBUTES] };
86
+ observer.observe(document.documentElement, options);
87
+ if (document.body) observer.observe(document.body, options);
88
+ const media = typeof window.matchMedia === "function" ? window.matchMedia(SYSTEM_DARK_QUERY2) : null;
89
+ media?.addEventListener("change", onChange);
90
+ return () => {
91
+ observer.disconnect();
92
+ media?.removeEventListener("change", onChange);
93
+ };
94
+ }
95
+ function usePortalTheme(defaultTheme = "light") {
96
+ const read = () => resolvePortalThemeFromDocument({ defaultTheme });
97
+ return useSyncExternalStore2(subscribe, read, () => defaultTheme);
98
+ }
99
+
75
100
  // src/tooltip.tsx
76
101
  import "react";
77
102
  import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
@@ -97,7 +122,7 @@ function TooltipTrigger({ ...props }) {
97
122
  return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
98
123
  }
99
124
  function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
100
- const portalTheme = resolvePortalThemeFromDocument();
125
+ const portalTheme = usePortalTheme();
101
126
  return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Positioner, { align, alignOffset, side, sideOffset, className: "z-[9999]", children: /* @__PURE__ */ jsxs(
102
127
  TooltipPrimitive.Popup,
103
128
  {
@@ -160,8 +185,8 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange, cla
160
185
  }
161
186
  function Sidebar({ side = "left", variant = "sidebar", collapsible = "icon", title = "Navigation", className, children, ...props }) {
162
187
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
188
+ const portalTheme = usePortalTheme();
163
189
  if (isMobile) {
164
- const portalTheme = resolvePortalThemeFromDocument();
165
190
  return /* @__PURE__ */ jsx2(Drawer.Root, { open: openMobile, onOpenChange: setOpenMobile, swipeDirection: side, children: /* @__PURE__ */ jsxs2(Drawer.Portal, { children: [
166
191
  /* @__PURE__ */ jsx2(Drawer.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0" }),
167
192
  /* @__PURE__ */ jsx2(Drawer.Viewport, { className: cn("fixed inset-y-0 z-50 flex", side === "left" ? "left-0" : "right-0"), children: /* @__PURE__ */ jsxs2(
package/dist/styles.css CHANGED
@@ -663,6 +663,36 @@
663
663
  --chalk-app-shadow-control: 0 6px 18px rgba(0, 0, 0, 0.7);
664
664
  }
665
665
 
666
+ [data-chalk][data-chalk-palette="cosmic-chalk"] {
667
+ --chalk-app-canvas: #080f20;
668
+ --chalk-app-chrome: #10182b;
669
+ --chalk-app-stage: #0b1427;
670
+ --chalk-app-panel: #10182b;
671
+ --chalk-app-tile-base: #10182b;
672
+ --chalk-app-tile-color-strength: 18%;
673
+ --chalk-app-text: #f4ecd7;
674
+ --chalk-app-text-muted: #aeb8c9;
675
+ --chalk-app-line: #2c3d59;
676
+ --chalk-app-line-strong: #415571;
677
+ --chalk-app-control: #121e34;
678
+ --chalk-app-control-hover: #192944;
679
+ --chalk-app-control-group: #0c162a;
680
+ --chalk-app-control-primary: #1b3953;
681
+ --chalk-app-control-primary-hover: #244b68;
682
+ --chalk-app-control-active: #172b45;
683
+ --chalk-app-control-active-line: #8fdcff;
684
+ --chalk-app-control-active-text: #f4ecd7;
685
+ --chalk-app-message-remote: #17243c;
686
+ --chalk-app-message-local: #1b3953;
687
+ --chalk-app-message-local-text: #f4ecd7;
688
+ --chalk-app-input: #0c1629;
689
+ --chalk-app-danger: #ff9ba8;
690
+ --chalk-app-danger-hover: #ffb4be;
691
+ --chalk-app-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.32);
692
+ --chalk-app-shadow-sm: 0 12px 36px rgba(0, 0, 0, 0.34);
693
+ --chalk-app-shadow-control: 0 7px 22px rgba(0, 0, 0, 0.42);
694
+ }
695
+
666
696
  [data-chalk][data-chalk-palette] {
667
697
  --background: var(--chalk-app-canvas);
668
698
  --foreground: var(--chalk-app-text);
@@ -716,67 +746,111 @@
716
746
  background-color: color-mix(in srgb, var(--chalk-participant-color) var(--chalk-app-tile-color-strength), var(--chalk-app-tile-base));
717
747
  }
718
748
 
719
- /* =============================================================================
720
- Base Styles & Scoped Reset
721
- ============================================================================= */
722
-
723
- :where([data-chalk], .chalk-root) {
724
- font-family: var(--chalk-font-family);
725
- color: var(--foreground);
726
- background: var(--background);
727
- line-height: 1.5;
728
- font-size: 16px;
729
- text-align: left;
730
- text-transform: none;
731
- letter-spacing: normal;
732
- text-size-adjust: 100%;
733
- -webkit-font-smoothing: antialiased;
734
- -moz-osx-font-smoothing: grayscale;
735
- isolation: isolate;
749
+ [data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"] :where(button, a, input, select, textarea, [tabindex]):focus-visible {
750
+ outline: 2px dashed var(--chalk-focus, #8fdcff);
751
+ outline-offset: 2px;
736
752
  }
737
753
 
738
- :where([data-chalk], .chalk-root),
739
- :where([data-chalk], .chalk-root) *,
740
- :where([data-chalk], .chalk-root) *::before,
741
- :where([data-chalk], .chalk-root) *::after {
742
- box-sizing: border-box;
754
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"]::before,
755
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"]::after {
756
+ content: "";
757
+ position: absolute;
758
+ pointer-events: none;
759
+ z-index: 0;
743
760
  }
744
761
 
745
- :where([data-chalk], .chalk-root) :where(h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd) {
746
- margin: 0;
762
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"]::before {
763
+ inset: 0;
764
+ opacity: 0.28;
765
+ background: radial-gradient(circle at 12% 18%, #8fdcff 0 1px, transparent 1.7px), radial-gradient(circle at 76% 13%, #f5d88c 0 1px, transparent 1.7px), radial-gradient(circle at 88% 72%, #8fdcff 0 1px, transparent 1.7px), radial-gradient(circle at 22% 84%, #f4ecd7 0 1px, transparent 1.7px);
747
766
  }
748
767
 
749
- :where([data-chalk], .chalk-root) :where(ul, ol) {
750
- margin: 0;
751
- padding: 0;
752
- list-style: none;
768
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"]::after {
769
+ inset: 9% 7%;
770
+ opacity: 0.14;
771
+ background: linear-gradient(24deg, transparent 0 49.4%, #8fdcff 49.6% 49.9%, transparent 50.1%), linear-gradient(156deg, transparent 0 49.6%, #f5d88c 49.8% 50%, transparent 50.2%);
753
772
  }
754
773
 
755
- :where([data-chalk], .chalk-root) :where(button, input, optgroup, select, textarea) {
756
- font: inherit;
757
- color: inherit;
758
- letter-spacing: inherit;
759
- text-transform: none;
760
- margin: 0;
774
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"] > * {
775
+ position: relative;
776
+ z-index: 1;
761
777
  }
762
778
 
763
- :where([data-chalk], .chalk-root) :where(button, [type="button"], [type="reset"], [type="submit"]) {
764
- appearance: none;
765
- -webkit-appearance: none;
779
+ @media (prefers-reduced-motion: reduce) {
780
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"]::before,
781
+ main[data-chalk][data-chalk-skin="chalk"][data-chalk-palette="cosmic-chalk"]::after {
782
+ opacity: 0.18;
783
+ }
766
784
  }
767
785
 
768
- :where([data-chalk], .chalk-root) :where(img, svg, video, canvas, audio, iframe, embed, object) {
769
- display: block;
770
- vertical-align: middle;
771
- }
786
+ /* =============================================================================
787
+ Base Styles & Scoped Reset
772
788
 
773
- :where([data-chalk], .chalk-root) :where(img) {
774
- max-width: 100%;
775
- height: auto;
776
- }
789
+ Lives in the `base` layer so that utilities keep beating it. Unlayered, a
790
+ zero-specificity `:where([data-chalk])` reset still outranks every utility,
791
+ which silently strips `bg-*` and `text-*` from any element that opens a
792
+ chalk scope.
793
+ ============================================================================= */
794
+
795
+ @layer base {
796
+ :where([data-chalk], .chalk-root) {
797
+ font-family: var(--chalk-font-family);
798
+ color: var(--foreground);
799
+ background: var(--background);
800
+ line-height: 1.5;
801
+ font-size: 16px;
802
+ text-align: left;
803
+ text-transform: none;
804
+ letter-spacing: normal;
805
+ text-size-adjust: 100%;
806
+ -webkit-font-smoothing: antialiased;
807
+ -moz-osx-font-smoothing: grayscale;
808
+ isolation: isolate;
809
+ }
777
810
 
778
- :where([data-chalk], .chalk-root) :where(video) {
779
- max-width: 100%;
811
+ :where([data-chalk], .chalk-root),
812
+ :where([data-chalk], .chalk-root) *,
813
+ :where([data-chalk], .chalk-root) *::before,
814
+ :where([data-chalk], .chalk-root) *::after {
815
+ box-sizing: border-box;
816
+ }
817
+
818
+ :where([data-chalk], .chalk-root) :where(h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd) {
819
+ margin: 0;
820
+ }
821
+
822
+ :where([data-chalk], .chalk-root) :where(ul, ol) {
823
+ margin: 0;
824
+ padding: 0;
825
+ list-style: none;
826
+ }
827
+
828
+ :where([data-chalk], .chalk-root) :where(button, input, optgroup, select, textarea) {
829
+ font: inherit;
830
+ color: inherit;
831
+ letter-spacing: inherit;
832
+ text-transform: none;
833
+ margin: 0;
834
+ }
835
+
836
+ :where([data-chalk], .chalk-root) :where(button, [type="button"], [type="reset"], [type="submit"]) {
837
+ appearance: none;
838
+ -webkit-appearance: none;
839
+ }
840
+
841
+ :where([data-chalk], .chalk-root) :where(img, svg, video, canvas, audio, iframe, embed, object) {
842
+ display: block;
843
+ vertical-align: middle;
844
+ }
845
+
846
+ :where([data-chalk], .chalk-root) :where(img) {
847
+ max-width: 100%;
848
+ height: auto;
849
+ }
850
+
851
+ :where([data-chalk], .chalk-root) :where(video) {
852
+ max-width: 100%;
853
+ }
780
854
  }
781
855
 
782
856
  /* =============================================================================
package/dist/toast.js CHANGED
@@ -4,6 +4,9 @@
4
4
  import * as React from "react";
5
5
  import { Toast as ToastPrimitive } from "@base-ui/react/toast";
6
6
 
7
+ // src/lib/use-portal-theme.ts
8
+ import { useSyncExternalStore } from "react";
9
+
7
10
  // src/lib/theme.ts
8
11
  var THEME_ATTRIBUTE_NAMES = ["data-chalk-theme", "data-theme"];
9
12
  var THEME_CLASS_NAMES = ["dark", "light"];
@@ -43,6 +46,28 @@ var resolvePortalThemeFromDocument = ({ defaultTheme = "light", allowSystem = tr
43
46
  return resolveThemeFromDocument({ defaultTheme, allowSystem });
44
47
  };
45
48
 
49
+ // src/lib/use-portal-theme.ts
50
+ var SYSTEM_DARK_QUERY2 = "(prefers-color-scheme: dark)";
51
+ var WATCHED_ATTRIBUTES = ["class", "data-chalk-theme", "data-theme"];
52
+ function subscribe(onChange) {
53
+ if (typeof document === "undefined") return () => {
54
+ };
55
+ const observer = new MutationObserver(onChange);
56
+ const options = { attributes: true, attributeFilter: [...WATCHED_ATTRIBUTES] };
57
+ observer.observe(document.documentElement, options);
58
+ if (document.body) observer.observe(document.body, options);
59
+ const media = typeof window.matchMedia === "function" ? window.matchMedia(SYSTEM_DARK_QUERY2) : null;
60
+ media?.addEventListener("change", onChange);
61
+ return () => {
62
+ observer.disconnect();
63
+ media?.removeEventListener("change", onChange);
64
+ };
65
+ }
66
+ function usePortalTheme(defaultTheme = "light") {
67
+ const read = () => resolvePortalThemeFromDocument({ defaultTheme });
68
+ return useSyncExternalStore(subscribe, read, () => defaultTheme);
69
+ }
70
+
46
71
  // src/lib/utils.ts
47
72
  import { clsx } from "clsx";
48
73
  import { twMerge } from "tailwind-merge";
@@ -85,7 +110,7 @@ var ToastAction = React.forwardRef(function ToastAction2({ className, ...props }
85
110
  });
86
111
  function ToastViewport({ className, children, ...props }) {
87
112
  const { toasts } = ToastPrimitive.useToastManager();
88
- const portalTheme = resolvePortalThemeFromDocument();
113
+ const portalTheme = usePortalTheme();
89
114
  return /* @__PURE__ */ jsxs(ToastPrimitive.Viewport, { "data-chalk": true, "data-chalk-theme": portalTheme, "data-slot": "toast-viewport", className: cn("chalk-ui-root chalk-ui-toast-viewport", className), ...props, children: [
90
115
  children,
91
116
  toasts.map((toastObject) => {
package/dist/tooltip.js CHANGED
@@ -11,6 +11,9 @@ function cn(...inputs) {
11
11
  return twMerge(clsx(inputs));
12
12
  }
13
13
 
14
+ // src/lib/use-portal-theme.ts
15
+ import { useSyncExternalStore } from "react";
16
+
14
17
  // src/lib/theme.ts
15
18
  var THEME_ATTRIBUTE_NAMES = ["data-chalk-theme", "data-theme"];
16
19
  var THEME_CLASS_NAMES = ["dark", "light"];
@@ -50,6 +53,28 @@ var resolvePortalThemeFromDocument = ({ defaultTheme = "light", allowSystem = tr
50
53
  return resolveThemeFromDocument({ defaultTheme, allowSystem });
51
54
  };
52
55
 
56
+ // src/lib/use-portal-theme.ts
57
+ var SYSTEM_DARK_QUERY2 = "(prefers-color-scheme: dark)";
58
+ var WATCHED_ATTRIBUTES = ["class", "data-chalk-theme", "data-theme"];
59
+ function subscribe(onChange) {
60
+ if (typeof document === "undefined") return () => {
61
+ };
62
+ const observer = new MutationObserver(onChange);
63
+ const options = { attributes: true, attributeFilter: [...WATCHED_ATTRIBUTES] };
64
+ observer.observe(document.documentElement, options);
65
+ if (document.body) observer.observe(document.body, options);
66
+ const media = typeof window.matchMedia === "function" ? window.matchMedia(SYSTEM_DARK_QUERY2) : null;
67
+ media?.addEventListener("change", onChange);
68
+ return () => {
69
+ observer.disconnect();
70
+ media?.removeEventListener("change", onChange);
71
+ };
72
+ }
73
+ function usePortalTheme(defaultTheme = "light") {
74
+ const read = () => resolvePortalThemeFromDocument({ defaultTheme });
75
+ return useSyncExternalStore(subscribe, read, () => defaultTheme);
76
+ }
77
+
53
78
  // src/tooltip.tsx
54
79
  import { jsx, jsxs } from "react/jsx-runtime";
55
80
  function TooltipProvider({ delay = 0, ...props }) {
@@ -73,7 +98,7 @@ function TooltipTrigger({ ...props }) {
73
98
  return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
74
99
  }
75
100
  function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
76
- const portalTheme = resolvePortalThemeFromDocument();
101
+ const portalTheme = usePortalTheme();
77
102
  return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Positioner, { align, alignOffset, side, sideOffset, className: "z-[9999]", children: /* @__PURE__ */ jsxs(
78
103
  TooltipPrimitive.Popup,
79
104
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q9labsai/chalk-ui",
3
- "version": "4.1.0",
3
+ "version": "4.1.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -179,7 +179,7 @@
179
179
  },
180
180
  "dependencies": {
181
181
  "@base-ui/react": "^1.0.0",
182
- "@q9labsai/chalk-assets": "^4.1.0",
182
+ "@q9labsai/chalk-assets": "^4.1.1",
183
183
  "class-variance-authority": "^0.7.1",
184
184
  "clsx": "^2.1.1",
185
185
  "tailwind-merge": "^3.6.0"