hazo_ui 4.7.0 → 4.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.
- package/CHANGE_LOG.md +34 -0
- package/README.md +43 -0
- package/dist/index.cjs +65 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +63 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGE_LOG.md
CHANGED
|
@@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v4.9.0 (2026-07-06)
|
|
9
|
+
|
|
10
|
+
### New
|
|
11
|
+
- **Theme Kit** — packaged, SSR-safe dark/light theme using the shadcn
|
|
12
|
+
`.dark`-class convention (toggles a `dark` class on `<html>`). New exports:
|
|
13
|
+
- `HazoThemeProvider` — context provider that keeps `<html class="dark">` in
|
|
14
|
+
sync with the resolved theme. Props `defaultTheme` (`"light" | "dark" |
|
|
15
|
+
"system"`, default `"system"`) and `storageKey` (default `"theme"`).
|
|
16
|
+
`window`/`document` are only touched inside `useEffect`, so no hydration
|
|
17
|
+
mismatch.
|
|
18
|
+
- `useTheme()` — `{ theme, setTheme, resolvedTheme, toggleTheme }`; throws
|
|
19
|
+
outside the provider.
|
|
20
|
+
- `ThemeToggle` — accessible Sun/Moon button; renders a stable icon until
|
|
21
|
+
mounted to avoid a first-render hydration mismatch.
|
|
22
|
+
- `ThemeScript` — inline no-flash script for the root `<head>`; sets the
|
|
23
|
+
`dark` class before first paint. Wrapped in try/catch so it can never
|
|
24
|
+
block rendering.
|
|
25
|
+
- Types: `Theme`, `ThemeProviderProps`, `ThemeContextValue`,
|
|
26
|
+
`ThemeScriptProps`, `ThemeToggleProps`.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// app/layout.tsx
|
|
30
|
+
import { HazoThemeProvider, ThemeScript } from 'hazo_ui';
|
|
31
|
+
|
|
32
|
+
<html lang="en" suppressHydrationWarning>
|
|
33
|
+
<head><ThemeScript storageKey="theme" /></head>
|
|
34
|
+
<body>
|
|
35
|
+
<HazoThemeProvider defaultTheme="system" storageKey="theme">
|
|
36
|
+
{children}
|
|
37
|
+
</HazoThemeProvider>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
40
|
+
```
|
|
41
|
+
|
|
8
42
|
## v4.7.0 (2026-07-01)
|
|
9
43
|
|
|
10
44
|
### New
|
package/README.md
CHANGED
|
@@ -214,6 +214,8 @@ The following components support both global config and prop-level color overrid
|
|
|
214
214
|
|
|
215
215
|
- **[Chart Primitives](#chart-primitives-v2170)** (v2.17.0) - Pure-SVG chart components for KPI cards and trend dashboards: `Sparkline`, `InverseSparkline`, `LineChart`, `MultiLineChart`, `StackedBars`, and `DateRangeSelector`. Zero third-party chart deps. Gap-aware paths, hover tooltips, and per-series endpoint markers built in.
|
|
216
216
|
|
|
217
|
+
- **[Theme Kit](#theme-kit-v490)** (v4.9.0) - SSR-safe dark/light theme using the shadcn `.dark`-class convention: `HazoThemeProvider`, `useTheme`, `ThemeToggle`, and a no-flash `ThemeScript`. Persists the chosen mode to `localStorage` and resolves `"system"` against the OS preference.
|
|
218
|
+
|
|
217
219
|
### State Primitives (v2.10.0)
|
|
218
220
|
|
|
219
221
|
Lightweight, opinionated components for the four ubiquitous async states: **loading**, **empty**, **error**, and **success**.
|
|
@@ -242,6 +244,47 @@ Lightweight, opinionated components for the four ubiquitous async states: **load
|
|
|
242
244
|
import { Slider, InputAffix, HazoUiProgressBar, HazoUiEtaProgress, useEtaProgress } from 'hazo_ui';
|
|
243
245
|
```
|
|
244
246
|
|
|
247
|
+
### Theme Kit (v4.9.0)
|
|
248
|
+
|
|
249
|
+
SSR-safe dark/light theme using the shadcn `.dark`-class convention (toggles a `dark` class on `<html>`). No hydration mismatch: `window`/`document` are only touched inside `useEffect`, and an inline `ThemeScript` sets the class before first paint to avoid a flash.
|
|
250
|
+
|
|
251
|
+
**Exports:** `HazoThemeProvider`, `useTheme`, `ThemeToggle`, `ThemeScript` and types `Theme`, `ThemeProviderProps`, `ThemeContextValue`, `ThemeScriptProps`, `ThemeToggleProps`.
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// app/layout.tsx
|
|
255
|
+
import { HazoThemeProvider, ThemeScript } from 'hazo_ui';
|
|
256
|
+
|
|
257
|
+
export default function RootLayout({ children }) {
|
|
258
|
+
return (
|
|
259
|
+
<html lang="en" suppressHydrationWarning>
|
|
260
|
+
<head>
|
|
261
|
+
<ThemeScript storageKey="theme" />
|
|
262
|
+
</head>
|
|
263
|
+
<body>
|
|
264
|
+
<HazoThemeProvider defaultTheme="system" storageKey="theme">
|
|
265
|
+
{children}
|
|
266
|
+
</HazoThemeProvider>
|
|
267
|
+
</body>
|
|
268
|
+
</html>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
// anywhere under the provider
|
|
275
|
+
import { ThemeToggle, useTheme } from 'hazo_ui';
|
|
276
|
+
|
|
277
|
+
function Header() {
|
|
278
|
+
const { theme, resolvedTheme, setTheme } = useTheme();
|
|
279
|
+
return <ThemeToggle />; // Sun/Moon button that flips light/dark
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
- `HazoThemeProvider` — context provider; keeps `<html class="dark">` in sync with the resolved theme. Props: `defaultTheme` (`"light" | "dark" | "system"`, default `"system"`), `storageKey` (default `"theme"`).
|
|
284
|
+
- `useTheme()` — `{ theme, setTheme, resolvedTheme, toggleTheme }`. Throws if used outside the provider.
|
|
285
|
+
- `ThemeToggle` — accessible Sun/Moon button; guards against a first-render hydration mismatch by rendering a stable icon until mounted.
|
|
286
|
+
- `ThemeScript` — inline no-flash script; render in the root `<head>` before content, with a `storageKey` matching the provider.
|
|
287
|
+
|
|
245
288
|
### shadcn/ui Primitive Re-exports
|
|
246
289
|
|
|
247
290
|
All shadcn/ui base components are re-exported from hazo_ui, so sibling hazo_* packages (and consumers) can import UI primitives from a single source without installing shadcn/ui separately:
|
package/dist/index.cjs
CHANGED
|
@@ -11391,6 +11391,67 @@ function EntryRow({ entry, on_select, onDelete }) {
|
|
|
11391
11391
|
}
|
|
11392
11392
|
);
|
|
11393
11393
|
}
|
|
11394
|
+
var ThemeContext = React26__namespace.createContext(void 0);
|
|
11395
|
+
function HazoThemeProvider({
|
|
11396
|
+
children,
|
|
11397
|
+
defaultTheme = "system",
|
|
11398
|
+
storageKey = "theme"
|
|
11399
|
+
}) {
|
|
11400
|
+
const [theme, setTheme] = useLocalStorage(storageKey, defaultTheme);
|
|
11401
|
+
const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
|
|
11402
|
+
const resolvedTheme = theme === "system" ? prefersDark ? "dark" : "light" : theme;
|
|
11403
|
+
React26__namespace.useEffect(() => {
|
|
11404
|
+
document.documentElement.classList.toggle("dark", resolvedTheme === "dark");
|
|
11405
|
+
}, [resolvedTheme]);
|
|
11406
|
+
const toggleTheme = React26__namespace.useCallback(() => {
|
|
11407
|
+
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
|
11408
|
+
}, [resolvedTheme, setTheme]);
|
|
11409
|
+
const value = React26__namespace.useMemo(
|
|
11410
|
+
() => ({ theme, setTheme, resolvedTheme, toggleTheme }),
|
|
11411
|
+
[theme, setTheme, resolvedTheme, toggleTheme]
|
|
11412
|
+
);
|
|
11413
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value, children });
|
|
11414
|
+
}
|
|
11415
|
+
function useTheme() {
|
|
11416
|
+
const context = React26__namespace.useContext(ThemeContext);
|
|
11417
|
+
if (context === void 0) {
|
|
11418
|
+
throw new Error("useTheme must be used within a <HazoThemeProvider>");
|
|
11419
|
+
}
|
|
11420
|
+
return context;
|
|
11421
|
+
}
|
|
11422
|
+
function ThemeScript({ storageKey = "theme" }) {
|
|
11423
|
+
const script = `(function(){try{var k=${JSON.stringify(
|
|
11424
|
+
storageKey
|
|
11425
|
+
)};var raw=localStorage.getItem(k);var t="system";if(raw){try{t=JSON.parse(raw);}catch(e){t=String(raw).replace(/^"|"$/g,"");}}var m=window.matchMedia("(prefers-color-scheme: dark)").matches;var isDark=t==="dark"||((t==="system"||!t)&&m);var c=document.documentElement.classList;if(isDark){c.add("dark");}else{c.remove("dark");}}catch(e){}})();`;
|
|
11426
|
+
return /* @__PURE__ */ jsxRuntime.jsx("script", { dangerouslySetInnerHTML: { __html: script } });
|
|
11427
|
+
}
|
|
11428
|
+
var ThemeToggle = React26__namespace.forwardRef(
|
|
11429
|
+
({ className, ...props }, ref) => {
|
|
11430
|
+
const { resolvedTheme, toggleTheme } = useTheme();
|
|
11431
|
+
const [mounted, setMounted] = React26__namespace.useState(false);
|
|
11432
|
+
React26__namespace.useEffect(() => {
|
|
11433
|
+
setMounted(true);
|
|
11434
|
+
}, []);
|
|
11435
|
+
const isDark = mounted && resolvedTheme === "dark";
|
|
11436
|
+
const label = isDark ? "Switch to light theme" : "Switch to dark theme";
|
|
11437
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11438
|
+
"button",
|
|
11439
|
+
{
|
|
11440
|
+
type: "button",
|
|
11441
|
+
ref,
|
|
11442
|
+
onClick: toggleTheme,
|
|
11443
|
+
"aria-label": label,
|
|
11444
|
+
className: cn(
|
|
11445
|
+
"inline-flex h-9 w-9 items-center justify-center rounded-full text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50",
|
|
11446
|
+
className
|
|
11447
|
+
),
|
|
11448
|
+
...props,
|
|
11449
|
+
children: isDark ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sun, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Moon, { className: "h-4 w-4" })
|
|
11450
|
+
}
|
|
11451
|
+
);
|
|
11452
|
+
}
|
|
11453
|
+
);
|
|
11454
|
+
ThemeToggle.displayName = "ThemeToggle";
|
|
11394
11455
|
|
|
11395
11456
|
Object.defineProperty(exports, "rawToast", {
|
|
11396
11457
|
enumerable: true,
|
|
@@ -11464,6 +11525,7 @@ exports.ErrorBanner = ErrorBanner;
|
|
|
11464
11525
|
exports.ErrorPage = ErrorPage;
|
|
11465
11526
|
exports.FunnelChart = FunnelChart;
|
|
11466
11527
|
exports.HazoContextProvider = HazoContextProvider;
|
|
11528
|
+
exports.HazoThemeProvider = HazoThemeProvider;
|
|
11467
11529
|
exports.HazoUiConfirmDialog = HazoUiConfirmDialog;
|
|
11468
11530
|
exports.HazoUiDialog = HazoUiDialog;
|
|
11469
11531
|
exports.HazoUiDialogClose = DialogClose;
|
|
@@ -11553,6 +11615,8 @@ exports.TabsContent = TabsContent;
|
|
|
11553
11615
|
exports.TabsList = TabsList;
|
|
11554
11616
|
exports.TabsTrigger = TabsTrigger;
|
|
11555
11617
|
exports.Textarea = Textarea;
|
|
11618
|
+
exports.ThemeScript = ThemeScript;
|
|
11619
|
+
exports.ThemeToggle = ThemeToggle;
|
|
11556
11620
|
exports.Toggle = Toggle;
|
|
11557
11621
|
exports.ToggleGroup = ToggleGroup;
|
|
11558
11622
|
exports.ToggleGroupItem = ToggleGroupItem;
|
|
@@ -11594,6 +11658,7 @@ exports.useLoadingState = useLoadingState;
|
|
|
11594
11658
|
exports.useLocalStorage = useLocalStorage;
|
|
11595
11659
|
exports.useMediaQuery = useMediaQuery;
|
|
11596
11660
|
exports.useSessionStorage = useSessionStorage;
|
|
11661
|
+
exports.useTheme = useTheme;
|
|
11597
11662
|
exports.useViewport = useViewport;
|
|
11598
11663
|
exports.useWakeLock = useWakeLock;
|
|
11599
11664
|
exports.use_fullscreen = use_fullscreen;
|