first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
# `<BrandingProvider>` and tenant branding hooks
## Purpose
`branding-context.tsx` ships the `<BrandingProvider>` React context provider
together with the hooks `useBranding`, `useTenantBranding`, and `useLogo`.
The provider's job is to resolve a `TenantBranding` payload (either static
via prop or fetched from a configurable endpoint) and to project that
payload onto two surfaces: (1) the `@suaveplan/react-ui` runtime
theme-token layer for every `--brand-*` CSS custom property, and (2) the
non-CSS DOM chrome — `document.title`, `<link rel="icon">`, and the tenant
custom-CSS `<style>` injection. The CSS path goes through
`useThemeMutator().setExtensionTokens(...)` so per-tenant tokens stack on
top of the active chassis theme exactly like any other mutator layer, with
automatic cleanup on unmount.
## Features
- Static branding via prop OR endpoint fetch via `brandingEndpoint` (+
optional `/tenantId` segment).
- Injectable `fetch: FetchLike` for testability.
- Zod validation (`TenantBrandingSchema.safeParse`) on every fetched
payload — Zod failures surface as `BRANDING_INVALID_PAYLOAD`.
- Network failures (transport throw or non-ok response) surface as
`BRANDING_FETCH_FAILED`.
- `--brand-primary`, `--brand-secondary`, `--brand-accent`, `--brand-font`
written via `setExtensionTokens` — cleanup is automatic on unmount.
- `document.title`, favicon `<link>`, and tenant `<style>` injection are
direct DOM writes (not CSS variables; no conflict with the mutator).
## Usage: Basic
```tsx
import { ThemeProvider, lightTheme } from "@suaveplan/react-ui/theme";
import { BrandingProvider } from "@suaveplan/react-auth-whitelabel";
<ThemeProvider themes={[lightTheme]} defaultTheme="light">
<BrandingProvider staticBranding={tenantBranding}>
<App />
</BrandingProvider>
</ThemeProvider>
```
## Usage: Advanced
```tsx
<BrandingProvider
brandingEndpoint="/api/branding"
tenantId={tenantSlug}
fetch={customFetch}
>
<App />
</BrandingProvider>
```
```tsx
const branding = useTenantBranding();
const lightLogo = useLogo(false);
const darkLogo = useLogo(true);
```
## API Reference
- `BrandingProvider(props)` — props include `brandingEndpoint?`,
`tenantId?`, `staticBranding?`, `fetch?`, `children`.
- `useBranding()` — returns `{ branding, isLoading, error, refresh }`.
- `useTenantBranding()` — returns the current `TenantBranding`.
- `useLogo(preferDark?)` — returns the most-appropriate logo URL or
`undefined`.
## Implementation Notes
The mutator effect runs on every `branding` change AND on every
`mutator` identity change. The mutator handle is stable across re-renders
(it's `useMemo`-wrapped inside `@suaveplan/react-ui`), so the effect only
re-fires when the branding payload actually changes. The cleanup
contract for `--brand-*` is delegated to `useThemeMutator` — when the
provider unmounts, the runtime invokes the mutator's `clear()` automatically,
removing every `--brand-*` declaration from `document.documentElement.style`.