useKanzoTheme
Read and write the runtime theme preferences owned by KanzoThemeProvider.
Usage
import { useKanzoTheme } from "@kanzo-tech/ui";const { radius, resolvedAppearance, 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
Three of the values come in pairs, because a preference can be the absence of one. appearance
is "" while the user is following the OS; resolvedAppearance is always light or dark.
Read the resolved value to draw something, and the preference only to say whether it was pinned.
themeByAppearance / resolvedTheme split the same way: 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({ radius: "lg" });
set({ density: "compact", font: "geist" });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.
API Reference
useKanzoTheme
function useKanzoTheme(): ThemeContextValue;Takes no arguments. Throws useKanzoTheme must be used within a <KanzoThemeProvider>. when no
provider is above it.
Returns
| Field | Type | What it is |
|---|---|---|
appearance | AppearancePref | The pinned side, or "" while the OS decides |
resolvedAppearance | "light" | "dark" | The side actually applied — read this to draw |
setAppearance | (next: AppearancePref) => void | Sets appearance, routing through a host theme manager if one is wired |
radius | "none" | "xs" | "sm" | "md" | "lg" | Corner radius axis |
density | "default" | "compact" | "comfortable" | Root font-size axis |
font | string | Sans typeface axis |
monoFont | string | Mono typeface axis |
themeByAppearance | Partial<Record<Appearance, string>> | Which theme this user wears on each side; {} defers to the tenant's default |
set | (patch: Partial<ThemePrefs>) => void | Merges a partial patch into the preferences and persists it |
fonts | FontOption[] | The sans options the host wired, for a selector |
monoFonts | FontOption[] | The mono options the host wired |
themes | ThemeOption[] | The themes the tenant published — [], never undefined. A flat list: a brand is a theme, so there is no second level |
defaultTheme | string | The name the applied side falls back to when the preference is empty |
defaultThemeFor | (side: Appearance) => string | The same answer for either side — a tenant may publish { light, dark }, since a theme carries its own palette |
resolvedTheme | string | This side's preference, or defaultTheme |
setTheme | (theme: string, options?: { appearance?: Appearance }) => void | Choose a theme for one side — the applied one unless appearance names the other |
retiredTheme | string | null | A theme the tenant withdrew out from under this user, held for the session |
corePrefs | Record<string, ResolvedPref & { decl }> | Every core axis resolved through the tenant's policy — value, via, offered |
sources | PrefSources | What this host published, in the shape a declaration names it by — themes |
reset | () => void | Unsets every axis, so the tenant's starting point applies again |
sections | Record<string, Record<string, string>> | What the user chose in the sections a host registered, keyed by namespace. Opaque: the core never parses it |
sectionPrefs | Record<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>) => void | Write 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. KanzoTheme and its
KanzoThemeProps set the axes on a subtree without a provider of their own; ThemeNotice and
ThemeNoticeProps are the notice a reader gets when the theme they had chosen is gone, and
ThemeRetiredCopy translates it — pass both title and the body composer or it ends up half
translated. 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. AppearanceController is where a foreign appearance model enters — next-themes
spells its third value "system" and this package spells it "", and that translation happens in
exactly one place.