hazo_ui 4.9.0 → 4.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/CHANGE_LOG.md CHANGED
@@ -5,6 +5,93 @@ 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.10.0 (2026-07-09)
9
+
10
+ ### New
11
+ - **`Logo`, `AppHeader`, `AppFooter` chrome components** (framework-neutral,
12
+ `"use client"`) — consume hazo_theme's `BrandIdentity` contract (type-only
13
+ import) plus the new `footer-public-*`/`footer-app-*` chrome tokens:
14
+ - `Logo({ logoUrl?, logoUrlDark?, appTitle?, href?, height?, className?,
15
+ imgClassName? })` — renders the light/dark logo image (swapped via
16
+ `dark:hidden`/`dark:block`), falling back to `appTitle` as text when no
17
+ `logoUrl` is set. Prop names intentionally mirror `BrandIdentity`'s fields
18
+ so a caller can spread an identity object directly: `<Logo {...identity}
19
+ />`.
20
+ - `AppHeader({ identity?, homeHref?, navItems?, actions?, className? })` — a
21
+ `bg-navbar`/`text-navbar-fg` header bar wrapping `Logo` plus nav/actions
22
+ slots.
23
+ - `AppFooter({ variant: 'public' | 'app', footer?, identity?, className? })`
24
+ — `'app'` renders the thin `bg-footer-app` strip (copyright + one link
25
+ row); `'public'` renders the taller multi-column `bg-footer-public` block
26
+ (brand row, link columns, social links), reading from `footer` or falling
27
+ back to `identity.footer[variant]`.
28
+ - All three are new exports from the package root; `hazo_theme` stays an
29
+ optional peer — the components don't import runtime code from it, only
30
+ the `BrandIdentity`/`FooterConfig` types.
31
+ - **`safeHref(url?: string): string | undefined`** exported from the package
32
+ root — scheme-allowlist for admin-configurable hrefs (`http://`, `https://`,
33
+ `mailto:`, or a same-origin `/path` that isn't protocol-relative `//host`;
34
+ anything else, including `javascript:`/`data:`, returns `undefined`). Every
35
+ `<a href>` sink inside the new chrome components (footer links, social
36
+ links) runs through it — XSS hardening for hrefs sourced from
37
+ admin-editable branding data.
38
+ - **`HazoUiDialog` header bar is now token-driven** — the header bar
39
+ background/foreground (previously hardcoded `#1e293b` background / white
40
+ text) now default to hazo_theme's `--dialog-header-bar-bg` /
41
+ `--dialog-header-bar-fg` tokens, and respect the existing per-variant
42
+ tinting (`headerColor`/`kind`) the same way the rest of the dialog does.
43
+ - **`HazoUiDialog` `kind` prop** (PRD hazo_theme §5.7) — orthogonal rendering/behavior
44
+ profile: `'form' | 'confirm' | 'status' | 'content'`. `kind` is a discriminated
45
+ union on `HazoUiDialogProps`: `title` is a required `string` for `'form'`/`'confirm'`,
46
+ and stays optional (defaulting to `"Action required"`) for `'status'`/`'content'`/
47
+ omitted — omitting `kind` entirely is byte-for-byte the pre-existing default
48
+ behavior (regression guard, verified via `npm run build` + `type-check`).
49
+ - `'form'` — comfortable body/label typography via the hazo_theme
50
+ `--dialog-body-size` / `--dialog-label-size` tokens.
51
+ - `'confirm'` — the compact centered-card layout previously hardcoded into
52
+ `HazoUiConfirmDialog` (see below).
53
+ - `'status'` — tinted header (via `--dialog-<variant>-bg/-fg`) with a single
54
+ acknowledge button (`showCancelButton=false`, `actionButtonText="OK"` by
55
+ default); `closeOnConfirm` defaults to `true` (self-closing) for this kind.
56
+ - `'content'` — unchanged sticky header/footer + scrollable body (this was
57
+ already the default structure).
58
+ - Added optional peer dependency `hazo_theme@^0.1.0` (`peerDependenciesMeta.hazo_theme.optional = true`).
59
+ hazo_ui does not import from hazo_theme; the `--dialog-*` CSS custom
60
+ properties referenced below only resolve when the CONSUMING app imports
61
+ `hazo_theme/tokens.css`.
62
+
63
+ ### Changed
64
+ - **`HazoUiConfirmDialog` is now a thin wrapper** over `<HazoUiDialog kind="confirm">`.
65
+ Its exported `HazoUiConfirmDialogProps` interface and `ConfirmDialogVariant` type
66
+ are UNCHANGED — zero prop-level breaking changes for existing consumers
67
+ (verified: `hazo_pay`'s `billing_admin_panel.tsx` and `hazo_config`'s
68
+ `app_config_list_editor.tsx` both still build with no changes on their end).
69
+ One documented behavior simplification: the old component allowed
70
+ independently-colored `accentColor` (border) vs. `confirmButtonColor` (button);
71
+ the new wrapper folds both into a single accent (`confirmButtonColor` wins if
72
+ both are set), since no known consumer relied on divergent values.
73
+ - **`VARIANT_PRESETS` (dialog) / `CONFIRM_VARIANT_PRESETS` (confirm dialog,
74
+ now deleted) hardcoded RGB → hazo_theme `--dialog-*` tokens.** This is a
75
+ **visual-only change** — flag it as a **major version bump** on the next
76
+ publish, since dialog/confirm-dialog appearance shifts for any consumer.
77
+ - Consumers that already import `hazo_theme/tokens.css` get the intended,
78
+ theme-consistent look automatically (dark/light + brand theme-sets).
79
+ - Consumers that do NOT import `hazo_theme/tokens.css` degrade gracefully:
80
+ an undefined CSS custom property used inside `hsl(var(--undefined-var))`
81
+ (or referenced directly, for the pre-wrapped `--dialog-<variant>-bg/-fg`
82
+ tokens) produces an invalid color value, which browsers simply ignore —
83
+ falling back to the previous/inherited value (e.g. transparent background,
84
+ inherited text color) rather than crashing or rendering `red`/broken
85
+ styles. Net effect: variant tinting silently disappears, dialogs still
86
+ render and function correctly. **Action for consumers wanting the
87
+ intended look: import `hazo_theme/tokens.css`.**
88
+ - hazo_theme exposes exactly one bg + one fg token per status (no separate
89
+ "header" vs. "description" shade), so the old two-tone split-header effect
90
+ is flattened to a single shade per variant.
91
+ - `kind="status"`/`kind="confirm"` at `variant="default"` (no per-status
92
+ token pair exists for "default") fall back to a neutral tint derived from
93
+ `--dialog-border` / `--dialog-fg` instead of leaving the header untinted.
94
+
8
95
  ## v4.9.0 (2026-07-06)
9
96
 
10
97
  ### New
package/README.md CHANGED
@@ -216,6 +216,8 @@ The following components support both global config and prop-level color overrid
216
216
 
217
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
218
 
219
+ - **[Chrome Components](#chrome-components-v4100)** (v4.10.0) - `Logo`, `AppHeader`, and `AppFooter` — framework-neutral header/footer/logo pieces that consume hazo_theme's `BrandIdentity` contract and its `footer-public-*`/`footer-app-*` chrome tokens. Every admin-configurable `<a href>` inside them runs through the new `safeHref()` scheme allowlist.
220
+
219
221
  ### State Primitives (v2.10.0)
220
222
 
221
223
  Lightweight, opinionated components for the four ubiquitous async states: **loading**, **empty**, **error**, and **success**.
@@ -250,6 +252,46 @@ SSR-safe dark/light theme using the shadcn `.dark`-class convention (toggles a `
250
252
 
251
253
  **Exports:** `HazoThemeProvider`, `useTheme`, `ThemeToggle`, `ThemeScript` and types `Theme`, `ThemeProviderProps`, `ThemeContextValue`, `ThemeScriptProps`, `ThemeToggleProps`.
252
254
 
255
+ ### Chrome Components (v4.10.0)
256
+
257
+ `Logo`, `AppHeader`, and `AppFooter` — framework-neutral chrome pieces that consume hazo_theme's
258
+ `BrandIdentity` (type-only import; `hazo_theme` stays an optional peer) plus its
259
+ `footer-public-*`/`footer-app-*` chrome tokens (import `hazo_theme/theme.css` for the tokens to
260
+ resolve).
261
+
262
+ ```tsx
263
+ import { AppHeader, AppFooter } from 'hazo_ui';
264
+ import type { BrandIdentity } from 'hazo_theme';
265
+
266
+ const identity: BrandIdentity = {
267
+ logoUrl: '/brand/logo.svg',
268
+ appTitle: 'Acme',
269
+ footer: { app: { copyright: '© 2026 Acme Inc.' } },
270
+ };
271
+
272
+ function Shell({ children }: { children: React.ReactNode }) {
273
+ return (
274
+ <>
275
+ <AppHeader identity={identity} homeHref="/" actions={<UserMenu />} />
276
+ {children}
277
+ <AppFooter variant="app" identity={identity} />
278
+ </>
279
+ );
280
+ }
281
+ ```
282
+
283
+ - `Logo({ logoUrl?, logoUrlDark?, appTitle?, href?, height?, className?, imgClassName? })` — logo
284
+ image (swaps light/dark variants via `dark:hidden`/`dark:block`) or text fallback.
285
+ - `AppHeader({ identity?, homeHref?, navItems?, actions?, className? })` — `bg-navbar` header bar.
286
+ - `AppFooter({ variant: 'public' | 'app', footer?, identity?, className? })` — `'app'` renders a
287
+ thin copyright/link strip (`bg-footer-app`); `'public'` renders a taller multi-column footer with
288
+ a brand row and social links (`bg-footer-public`).
289
+ - **`safeHref(url?: string): string | undefined`** — also exported standalone from the package
290
+ root. Allows `http://`, `https://`, `mailto:`, and same-origin `/path` hrefs; returns `undefined`
291
+ for anything else (`javascript:`, `data:`, protocol-relative `//host`, …). Used internally by
292
+ every chrome-component `<a href>` sink; reach for it directly when rendering admin-configurable
293
+ links elsewhere.
294
+
253
295
  ```tsx
254
296
  // app/layout.tsx
255
297
  import { HazoThemeProvider, ThemeScript } from 'hazo_ui';