Kanzo UI
Hooks

useKanzoTheme

Read and write the runtime theme preferences owned by KanzoThemeProvider.

Usage

import { useKanzoTheme } from "@kanzo-tech/ui";
const { density, appearance, set } = useKanzoTheme();

It throws outside a KanzoThemeProvider — there is no silent default, because a component reading a theme that nothing applies is a bug, not a fallback.

The provider writes the preferences to <html> as data-* attributes and toggles .dark. This hook is how you read that state and request a change; it never touches the DOM itself.

Preference and resolution

appearance is always light or dark: the side the person picked, or else the tenant's starting side, or else the OS's prefers-color-scheme. The OS is a starting point and is never stored.

themeByAppearance / resolvedTheme are a preference and its resolution: an empty preference defers to whichever theme the tenant makes default. The preference is a map keyed by side, because a theme is a side — "which theme" without "on which side" does not name a choice.

Changing a preference

set takes a partial patch and merges it — pass one axis, not the whole object:

const { set } = useKanzoTheme();

set({ density: "compact" });
set({ appearance: "dark", density: "comfortable" });

Appearance has its own setter, setAppearance, because a host theme manager (next-themes) may own that axis. Going through set({ appearance }) would bypass the host and leave the two disagreeing.

The retired theme as a toast

When a tenant withdraws the theme a reader had chosen, the provider clears the choice and holds its name in retiredTheme for the session, and the theme cards say so in an alert. Where the cards are not drawn — a tenant that withholds the theme — a host that wants it said reads the same state and toasts it once, beside its own <Toaster />. The library ships no component for it: what it would be is the effect below under a name, and no product had called one.

API Reference

useKanzoTheme

function useKanzoTheme(): ThemeContextValue;

Takes no arguments. Throws useKanzoTheme must be used within a <KanzoThemeProvider>. when no provider is above it.

Returns

FieldTypeWhat it is
appearance"light" | "dark"The side being worn
setAppearance(next: Appearance) => voidWears that side
density"default" | "compact" | "comfortable"How big everything is, relative to the browser's font size
themeByAppearancePartial<Record<Appearance, string>>Which theme this user wears on each side; {} defers to the tenant's default
set(patch: Partial<ThemePrefs>) => voidMerges a partial patch into the preferences and persists it
themesThemeOption[]The themes the tenant published — [], never undefined. A flat list: a brand is a theme, so there is no second level
defaultThemestringThe name the applied side falls back to when the preference is empty
defaultThemeFor(side: Appearance) => stringThe same answer for either side — a tenant may publish { light, dark }, since a theme carries its own palette
resolvedThemestringThis side's preference, or defaultTheme
setTheme(theme: string, options?: { appearance?: Appearance }) => voidChoose a theme for one side — the applied one unless appearance names the other
retiredThemestring | nullA theme the tenant withdrew out from under this user, held for the session
corePrefsRecord<string, ResolvedPref & { decl }>Every core axis resolved through the tenant's policy — value, via, offered
reset() => voidUnsets every axis, so the tenant's starting point applies again
sectionsRecord<string, Record<string, string>>What the user chose in the sections a host registered, keyed by namespace. Opaque: the core never parses it
sectionPrefsRecord<string, Record<string, ResolvedPref & { decl }>>Those same choices resolved — value, via, offered — each with its declaration beside it
setSectionPref(namespace, values: Record<string, string | undefined>) => voidWrite into one namespace. Every other namespace rides through untouched

The preference fields are exactly ThemePrefs, so set accepts any subset of them.

sections and sectionPrefs are not the same thing

sections is the stored map — what the user picked, and nothing else. sectionPrefs is what is applied, after a tenant's policy and the manifest's default have had their say, so the two disagree whenever a choice is pinned or withheld. Read sectionPrefs to draw anything; sections only to say what the user themselves chose. See Contributing a section.

KanzoThemeProviderProps is exported, so a wrapper can take the same props without restating them.

The rest of the theme surface, for a host that builds its own controls. retiredTheme is the theme a reader had chosen and the tenant withdrew, which the theme cards say in an alert and a host may also toast (below). ThemeStorage is the two-method adapter the provider reads and writes through: get returns a sparse document, because a key is absent when nobody has chosen it, not when it is at its default. ThemeScriptOptions is what the blocking script takes, and its storageKey must match the provider's.

On this page