Appearance
The desktop app's runtime theme as a React provider. Themes, light and dark pairing, contrast, accent, fonts, the code theme and the motion level, kept by a host.
zidane-ui/appearance is the desktop app's runtime theme. AppearanceProvider reads the user's preferences from an AppearanceHost, picks the active theme (the OS's appearance under system), derives its palette and writes the --zg-* variables on <html>, with the contrast, the accent override and the fonts. It also puts the tree under the motion preference, and stores a snapshot of the variables so the next page load paints the right theme before any script runs. Each frame below runs on its own fixture host, scoped to the frame.
Mount it#
Wrap the app once. In a web app, the preferences live in localStorage.
import 'zidane-ui/styles.css'
import { createBrowserAppearanceHost } from 'zidane-ui/adapters'
import { AppearanceProvider, codeThemeTypeOf } from 'zidane-ui/appearance'
const appearance = createBrowserAppearanceHost({ codeThemeType: codeThemeTypeOf })
export function Root() {
return (
<AppearanceProvider host={appearance}>
<App />
</AppearanceProvider>
)
}AppHostProvider (zidane-ui/app) puts host.appearance in an AppearanceProvider for you.
| Prop | Does |
|---|---|
host | Where the preferences live, the extra themes, the snapshot and the window effects |
folder, themesKey | The folder whose extra themes are offered (host.themes), and a key that changes when they may have changed |
legacyThemeId | A theme id from zidane's shared settings, read once on the first run to seed the pairing |
root | Where the variables go. Default <html>. Anywhere else, no snapshot is written and the window is left alone |
motion | Put the tree under the motion preference (MotionProvider). Default on |
Pick a host#
| Host | From | Keeps |
|---|---|---|
createBrowserAppearanceHost() | zidane-ui/adapters | localStorage, under the desktop app's keys. Another tab's change is adopted. zidane-server keeps no appearance, so the web app offers the built-in themes only. |
createFixtureAppearanceHost(options) | zidane-ui/appearance/testing | Memory, for demos and tests. It records the snapshot and the window calls. |
| Your own | zidane-ui/appearance/headless | Anything. createStorageAppearancePrefs and createStorageBoot keep the preferences and the snapshot in any Storage. |
What a host offers#
Only prefs is required.
| Member | Gives |
|---|---|
prefs | The preferences: get, subscribe, set, reset, isFirstRun |
themes(folder) | Themes beyond the built-in ones (the desktop app lists the folder's extension themes) |
boot.write(snapshot) | Stores the applied variables for the pre-paint script |
window.setBackground, window.setZoom, window.setNativeTheme | Native effects: the resize background, the interface scale, the title bar's appearance |
The snapshot is { appearance, vars } under zg:appearance:boot:v1. A page replays it before its bundle loads:
<script>
try {
const s = JSON.parse(localStorage.getItem('zg:appearance:boot:v1'))
for (const [k, v] of Object.entries(s.vars)) document.documentElement.style.setProperty(k, v)
document.documentElement.classList.add(s.appearance)
} catch {}
</script>Read it#
| Hook | Returns |
|---|---|
useAppearance() | The preferences, the active appearance and theme, every selectable theme, and the setters (set, setMode, pickTheme, reset) |
useAppearanceState() | The preferences alone |
useTheme(), useColors(), usePalette() | The active theme, its colors, the derived palette |
useShikiTheme() | The code theme: the pick for the current appearance, else the theme's own |
useAppearanceMotion(), useAppearanceReducedMotion() | The motion level the preference resolves to |
Outside a provider the hooks return the defaults and the setters do nothing. parseAppearance validates a stored blob field by field, LIMITS holds the bounds, fontsToCssVars and availableCuratedFonts are the font tokens and the installed-font check, and codeThemesOf(appearance) lists the bundled code themes.
Test your host#
import { describeAppearanceHostContract } from 'zidane-ui/appearance/testing/contract'
describeAppearanceHostContract('my host', backend => makeHarness(backend), ['boot'])| Entry point | Holds |
|---|---|
zidane-ui/appearance | AppearanceProvider, the hooks, applyPalette, codeThemesOf |
zidane-ui/appearance/headless | AppearanceHost, the preferences model, the Storage stores, the font tokens, zoomKeyCommands(prefs) (the zoom keys for useKeyCommands) |
zidane-ui/appearance/testing | createFixtureAppearanceHost |
zidane-ui/appearance/testing/contract | describeAppearanceHostContract |