@nexub/react 1.8.0 → 1.9.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.
@@ -36,6 +36,64 @@ function readGlass(key, fallback) {
36
36
  }
37
37
  return fallback;
38
38
  }
39
+ var CONTRAST_QUERY = "(prefers-contrast: more)";
40
+ function readContrast(root) {
41
+ const attr = root.getAttribute("data-contrast");
42
+ if (attr === "standard" || attr === "medium" || attr === "high") return attr;
43
+ return window.matchMedia(CONTRAST_QUERY).matches ? "high" : "standard";
44
+ }
45
+ function useSeedTheme(seed, target) {
46
+ React.useEffect(() => {
47
+ if (!seed) return;
48
+ const color = seed;
49
+ const root = document.documentElement;
50
+ const mq = window.matchMedia(CONTRAST_QUERY);
51
+ const observer = new MutationObserver(() => update());
52
+ let applied = [];
53
+ let create;
54
+ let done = false;
55
+ const clear = () => {
56
+ for (const name of applied) root.style.removeProperty(name);
57
+ applied = [];
58
+ };
59
+ const stop = () => {
60
+ done = true;
61
+ observer.disconnect();
62
+ mq.removeEventListener("change", update);
63
+ };
64
+ function update() {
65
+ if (done || !create) return;
66
+ let vars;
67
+ try {
68
+ vars = create(color, { contrast: readContrast(root), target });
69
+ } catch (error) {
70
+ console.error(error);
71
+ clear();
72
+ stop();
73
+ return;
74
+ }
75
+ clear();
76
+ for (const [name, value] of Object.entries(vars)) root.style.setProperty(name, value);
77
+ applied = Object.keys(vars);
78
+ }
79
+ observer.observe(root, { attributes: true, attributeFilter: ["data-contrast"] });
80
+ mq.addEventListener("change", update);
81
+ import("@nexub/tokens/seed").then(
82
+ (mod) => {
83
+ create = mod.createSeedTheme;
84
+ update();
85
+ },
86
+ (error) => {
87
+ console.error(error);
88
+ stop();
89
+ }
90
+ );
91
+ return () => {
92
+ stop();
93
+ clear();
94
+ };
95
+ }, [seed, target]);
96
+ }
39
97
  function writeStorage(key, value) {
40
98
  if (typeof window === "undefined") return;
41
99
  try {
@@ -48,8 +106,11 @@ function ThemeProvider({
48
106
  defaultTheme = "system",
49
107
  storageKey = "nexub-theme",
50
108
  defaultGlass = "balanced",
51
- glassStorageKey = "nexub-glass"
109
+ glassStorageKey = "nexub-glass",
110
+ seed,
111
+ seedTarget = "primary"
52
112
  }) {
113
+ useSeedTheme(seed, seedTarget);
53
114
  const [theme, setThemeState] = React.useState(defaultTheme);
54
115
  const [systemTheme, setSystemTheme] = React.useState("light");
55
116
  const [glass, setGlassState] = React.useState(defaultGlass);
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
  import {
3
3
  FormField
4
- } from "../../chunk-G4RLUGE3.js";
5
- import "../../chunk-ZLW7IBXK.js";
4
+ } from "../../chunk-LYOU2D5N.js";
6
5
  import "../../chunk-PGD246WD.js";
7
6
  import "../../chunk-QVTVDL6Y.js";
7
+ import "../../chunk-ZLW7IBXK.js";
8
8
  import "../../chunk-22BGPRJZ.js";
9
9
  import "../../chunk-5W3AOOYS.js";
10
10
  import "../../chunk-DEIBF3P7.js";
@@ -3,7 +3,7 @@ import {
3
3
  ThemeProvider,
4
4
  ThemeScript,
5
5
  useTheme
6
- } from "../../chunk-AEBYQKLS.js";
6
+ } from "../../chunk-3XX45SUC.js";
7
7
  import "../../chunk-NHUQLIHL.js";
8
8
  export {
9
9
  ThemeProvider,
package/dist/index.d.ts CHANGED
@@ -1788,8 +1788,22 @@ interface ThemeProviderProps {
1788
1788
  defaultGlass?: GlassMode;
1789
1789
  /** localStorage key for the glass intensity. Defaults to 'nexub-glass'. */
1790
1790
  glassStorageKey?: string;
1791
+ /**
1792
+ * Brand seed colour (`#rgb` / `#rrggbb`) for white-label theming. The accent
1793
+ * family named by `seedTarget` is regenerated from it (same HCT tones as the
1794
+ * built-in roles, so WCAG pairs hold) and set on `<html>` via the CSSOM
1795
+ * (strict-CSP safe), following the current `data-contrast` level.
1796
+ *
1797
+ * Runs on the client after `@nexub/tokens/seed` loads (a lazy chunk with
1798
+ * the colour library), so server-rendered pages paint the default brand
1799
+ * first. To avoid that flash, emit `createSeedThemeCss(seed)` from
1800
+ * `@nexub/tokens/seed` into a server-rendered stylesheet instead.
1801
+ */
1802
+ seed?: string;
1803
+ /** Accent family the seed recolours: 'primary' (default) or 'tertiary' (product brand). */
1804
+ seedTarget?: 'primary' | 'tertiary';
1791
1805
  }
1792
- declare function ThemeProvider({ children, defaultTheme, storageKey, defaultGlass, glassStorageKey, }: ThemeProviderProps): React.JSX.Element;
1806
+ declare function ThemeProvider({ children, defaultTheme, storageKey, defaultGlass, glassStorageKey, seed, seedTarget, }: ThemeProviderProps): React.JSX.Element;
1793
1807
  declare function useTheme(): ThemeContextValue;
1794
1808
  interface ThemeScriptProps {
1795
1809
  /** Must match ThemeProvider's `storageKey`. Defaults to 'nexub-theme'. */
package/dist/index.js CHANGED
@@ -1,10 +1,8 @@
1
1
  'use client';
2
- import {
3
- TopNav
4
- } from "./chunk-KRHD7VI6.js";
5
2
  import {
6
3
  Root
7
4
  } from "./chunk-ZXLACKEB.js";
5
+ import "./chunk-XYXMPHND.js";
8
6
  import {
9
7
  Textarea,
10
8
  textareaVariants
@@ -13,7 +11,7 @@ import {
13
11
  ThemeProvider,
14
12
  ThemeScript,
15
13
  useTheme
16
- } from "./chunk-AEBYQKLS.js";
14
+ } from "./chunk-3XX45SUC.js";
17
15
  import {
18
16
  ThinkingIndicator
19
17
  } from "./chunk-ESMIBOF5.js";
@@ -34,7 +32,9 @@ import {
34
32
  import {
35
33
  Toolbar
36
34
  } from "./chunk-2ZBJOUJ7.js";
37
- import "./chunk-XYXMPHND.js";
35
+ import {
36
+ TopNav
37
+ } from "./chunk-KRHD7VI6.js";
38
38
  import {
39
39
  StatusPill,
40
40
  statusPillVariants
@@ -180,7 +180,6 @@ import {
180
180
  import {
181
181
  NavigationRail
182
182
  } from "./chunk-5GN2GFW3.js";
183
- import "./chunk-PICMOOAZ.js";
184
183
  import "./chunk-ZT3NWE5U.js";
185
184
  import "./chunk-CAOLGXA5.js";
186
185
  import {
@@ -195,6 +194,10 @@ import {
195
194
  inputWrapperVariants
196
195
  } from "./chunk-F7ZA2VA2.js";
197
196
  import "./chunk-KLTQJZOX.js";
197
+ import {
198
+ Heading,
199
+ headingVariants
200
+ } from "./chunk-SKIJVVBU.js";
198
201
  import {
199
202
  KeyboardShortcut
200
203
  } from "./chunk-W2P2WOAN.js";
@@ -216,10 +219,7 @@ import {
216
219
  import "./chunk-ADO2DB6Q.js";
217
220
  import {
218
221
  FormField
219
- } from "./chunk-G4RLUGE3.js";
220
- import {
221
- HelperText
222
- } from "./chunk-ZLW7IBXK.js";
222
+ } from "./chunk-LYOU2D5N.js";
223
223
  import {
224
224
  Label,
225
225
  labelVariants
@@ -234,10 +234,10 @@ import {
234
234
  Grid
235
235
  } from "./chunk-6JBQAJJD.js";
236
236
  import "./chunk-VLQGQFW7.js";
237
+ import "./chunk-PICMOOAZ.js";
237
238
  import {
238
- Heading,
239
- headingVariants
240
- } from "./chunk-SKIJVVBU.js";
239
+ HelperText
240
+ } from "./chunk-ZLW7IBXK.js";
241
241
  import {
242
242
  DateRangePicker
243
243
  } from "./chunk-6U5WQERX.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexub/react",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "React component library for Nexub Design System — 90+ accessible, themeable UI components built on Radix UI primitives.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -68,12 +68,12 @@
68
68
  "date-fns": "^4.4.0",
69
69
  "react-day-picker": "^9.14.0",
70
70
  "tailwind-merge": "^2.6.1",
71
- "@nexub/icons": "1.8.0"
71
+ "@nexub/icons": "1.9.0"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "react": ">=18 <20",
75
75
  "react-dom": ">=18 <20",
76
- "@nexub/tokens": "^1.8.0"
76
+ "@nexub/tokens": "^1.9.0"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@playwright/test": "1.64.0",
@@ -98,8 +98,8 @@
98
98
  "vaul": "1.1.2",
99
99
  "vite": "^7.3.7",
100
100
  "vitest": "^5.0.5",
101
- "@nexub/themes": "1.8.0",
102
- "@nexub/tokens": "1.8.0"
101
+ "@nexub/themes": "1.9.0",
102
+ "@nexub/tokens": "1.9.0"
103
103
  },
104
104
  "scripts": {
105
105
  "build": "tsup && node scripts/check-dts.mjs",
@@ -1,13 +1,13 @@
1
1
  'use client';
2
- import {
3
- HelperText
4
- } from "./chunk-ZLW7IBXK.js";
5
2
  import {
6
3
  Label
7
4
  } from "./chunk-PGD246WD.js";
8
5
  import {
9
6
  FormError
10
7
  } from "./chunk-QVTVDL6Y.js";
8
+ import {
9
+ HelperText
10
+ } from "./chunk-ZLW7IBXK.js";
11
11
  import {
12
12
  cn
13
13
  } from "./chunk-Q7G5WGYV.js";