# zidane-ui/appearance/headless

Import: `import { … } from 'zidane-ui/appearance/headless'`. Source: [ui/src/appearance/headless/index.ts](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/index.ts).

## Functions

### `activeThemeId`

Function.

The theme id to show: the dark slot when the mode (or the OS, under `system`) asks for dark, else the light slot.

```ts
function activeThemeId(state: AppearanceState, prefersDark: boolean): string
```

**Parameters**

- `state` (`AppearanceState`): The preferences.
- `prefersDark` (`boolean`): Whether the OS prefers dark.

**Returns** `string`

Source: [ui/src/appearance/headless/prefs.ts:236](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L236)

### `availableCuratedFonts`

Function.

The curated fonts installed on this computer.

```ts
function availableCuratedFonts(): FontOption[]
```

**Returns** `FontOption[]`

Source: [ui/src/appearance/headless/fonts.ts:171](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L171)

### `createStorageAppearancePrefs`

Function.

Appearance preferences kept in a `Storage` under one key, read on first use and validated with `parseAppearance`.
Another window's change (the `storage` event) is adopted and announced.

```ts
function createStorageAppearancePrefs(
  options: StorageAppearancePrefsOptions,
): AppearancePrefs
```

**Parameters**

- `options` (`StorageAppearancePrefsOptions`): The storage, the key and how to check code theme ids.

**Returns** `AppearancePrefs`

Source: [ui/src/appearance/headless/storage.ts:47](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/storage.ts#L47)

### `createStorageBoot`

Function.

The pre-paint snapshot kept in a `Storage` as a flat JSON blob (`{ appearance, vars }`), so the page's inline
script needs no logic beyond `setProperty`.

```ts
function createStorageBoot(storage: AppearanceStorage, key?: string): AppearanceBoot
```

**Parameters**

- `storage` (`AppearanceStorage`): Where to keep it, such as `localStorage`.
- `key?` (`string`, default `BOOT_SNAPSHOT_KEY`): The key. Default: [`BOOT_SNAPSHOT_KEY`](https://zidane.si/docs/reference/ui-appearance-headless.md#BOOT_SNAPSHOT_KEY).

**Returns** `AppearanceBoot`

Source: [ui/src/appearance/headless/storage.ts:117](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/storage.ts#L117)

### `cssFamily`

Function.

Quotes a family name for CSS when it contains spaces or punctuation.

```ts
function cssFamily(family: string): string
```

**Parameters**

- `family` (`string`): The family name.

**Returns** `string`

Source: [ui/src/appearance/headless/fonts.ts:72](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L72)

### `fontStack`

Function.

A font stack with the chosen family first, so a font that disappears degrades to the default stack.

```ts
function fontStack(choice: string | null, kind: 'ui' | 'mono'): string
```

**Parameters**

- `choice` (`string | null`): The chosen family, or `null` for the default stack.
- `kind` (`'ui' | 'mono'`): Which default stack follows it.

**Returns** `string`

Source: [ui/src/appearance/headless/fonts.ts:82](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L82)

### `fontsToCssVars`

Function.

The font CSS variables for the picked families and sizes.

```ts
function fontsToCssVars(fonts: FontChoices, sizes: FontSizes): FontCssVars
```

**Parameters**

- `fonts` (`FontChoices`): The families per role.
- `sizes` (`FontSizes`): The sizes per role.

**Returns** `FontCssVars`

Source: [ui/src/appearance/headless/fonts.ts:105](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L105)

### `guessKind`

Function.

Guesses a family's kind from its name (the Local Font Access API doesn't classify).

```ts
function guessKind(family: string): 'mono' | 'sans' | 'serif'
```

**Parameters**

- `family` (`string`): The family name.

**Returns** `'mono' | 'sans' | 'serif'`

Source: [ui/src/appearance/headless/fonts.ts:216](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L216)

### `isFontAvailable`

Function.

True when `family` renders differently from both generic fallbacks,
i.e. the OS resolved it to an actual installed face. Cheap (three
`measureText` calls), synchronous, no permission needed.

```ts
function isFontAvailable(family: string): boolean
```

**Parameters**

- `family` (`string`): The family name.

**Returns** `boolean`

Source: [ui/src/appearance/headless/fonts.ts:146](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L146)

### `parseAppearance`

Function.

Validates a stored blob field by field; anything unrecognised falls back to its default.

```ts
function parseAppearance(
  raw: string,
  options?: ParseAppearanceOptions,
): AppearanceState | undefined
```

**Parameters**

- `raw` (`string`): The stored JSON.
- `options?` (`ParseAppearanceOptions`, default `{}`): How to check code theme ids.

**Returns** `AppearanceState | undefined`: The preferences, or `undefined` when `raw` is not a known blob.

Source: [ui/src/appearance/headless/prefs.ts:158](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L158)

### `queryInstalledFonts`

Function.

Installed families via the Local Font Access API. Resolves to `[]` when
unsupported or denied. Families are de-duplicated and sorted; `kind` is
guessed from the name because the API doesn't classify.

```ts
function queryInstalledFonts(): Promise<FontOption[]>
```

**Returns** `Promise<FontOption[]>`

Source: [ui/src/appearance/headless/fonts.ts:185](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L185)

### `resolveMotion`

Function.

The motion level a preference resolves to: `system` follows the OS, an explicit choice wins.

```ts
function resolveMotion(
  pref: MotionPreference,
  osReducedMotion: boolean,
): 'off' | 'reduced' | 'full'
```

**Parameters**

- `pref` (`MotionPreference`): The stored preference.
- `osReducedMotion` (`boolean`): Whether the OS asks for reduced motion.

**Returns** `'off' | 'reduced' | 'full'`

Source: [ui/src/appearance/headless/prefs.ts:247](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L247)

### `stepInterfaceScale`

Function.

The preferences with the interface scale one step up or down, or back to 1, clamped to [`LIMITS`](https://zidane.si/docs/reference/ui-appearance-headless.md#LIMITS).

```ts
function stepInterfaceScale(
  prev: AppearanceState,
  direction: -1 | 0 | 1,
): AppearanceState
```

**Parameters**

- `prev` (`AppearanceState`): The current preferences.
- `direction` (`-1 | 0 | 1`): `1` one step up, `-1` one step down, `0` back to 100%.

**Returns** `AppearanceState`

Source: [ui/src/appearance/headless/prefs.ts:222](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L222)

### `zoomKeyCommands`

Function.

The zoom key commands over a host's preferences.

```ts
function zoomKeyCommands(prefs: Pick<AppearancePrefs, 'set'>): KeyCommands
```

**Parameters**

- `prefs` (`Pick<AppearancePrefs, 'set'>`): The appearance host's preferences.

**Returns** `KeyCommands`

Source: [ui/src/appearance/headless/zoom-commands.ts:15](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/zoom-commands.ts#L15)

## Constants

### `APPEARANCE_STORAGE_KEY`

Constant.

The key the preferences live under.

```ts
const APPEARANCE_STORAGE_KEY = 'zg:appearance:v1'
```

Source: [ui/src/appearance/headless/storage.ts:11](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/storage.ts#L11)

### `BOOT_SNAPSHOT_KEY`

Constant.

The key the pre-paint snapshot lives under. A page's inline script replays it before any bundle loads.

```ts
const BOOT_SNAPSHOT_KEY = 'zg:appearance:boot:v1'
```

Source: [ui/src/appearance/headless/storage.ts:14](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/storage.ts#L14)

### `CURATED_FONTS`

Constant.

Curated candidates. Order = display order within each kind. Availability
is checked at runtime; missing ones are hidden from the picker.

```ts
const CURATED_FONTS: readonly FontOption[]
```

Source: [ui/src/appearance/headless/fonts.ts:35](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L35)

### `DEFAULT_APPEARANCE`

Constant.

The preferences before the user changes anything.

```ts
const DEFAULT_APPEARANCE: AppearanceState
```

Source: [ui/src/appearance/headless/prefs.ts:99](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L99)

### `FONT_STACKS`

Constant.

The default font stacks: the platform UI stack and `ui-monospace`.

```ts
const FONT_STACKS: { readonly mono: 'ui-monospace, \'SF Mono\', SFMono-Regular, Menlo, Monaco, Consolas, \'Liberation Mono\', monospace'; readonly ui: '-apple-system, BlinkMacSystemFont, \'Segoe UI\', system-ui, Roboto, sans-serif' }
```

Source: [ui/src/appearance/headless/fonts.ts:16](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L16)

### `LIMITS`

Constant.

The bounds (and slider steps) of the numeric preferences.

```ts
const LIMITS: { readonly contrast: { readonly max: 200; readonly min: 50; readonly step: 5 }; readonly fontSize: { readonly max: 22; readonly min: 10; readonly step: 1 }; readonly interfaceScale: { readonly max: 1.5; readonly min: 0.8; readonly step: 0.05 }; readonly panelMs: { readonly max: 400; readonly min: 0; readonly step: 20 }; readonly panelWidth: { readonly max: 900; readonly min: 300 }; readonly sidebarWidth: { readonly max: 480; readonly min: 220 } }
```

Source: [ui/src/appearance/headless/prefs.ts:119](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L119)

## Interfaces

### `AppearanceBoot`

Interface.

Where the pre-paint snapshot goes.

```ts
interface AppearanceBoot
```

**Properties**

- `write` (`(snapshot: BootSnapshot) => void`): Stores the snapshot (after every apply that changed something).

Source: [ui/src/appearance/headless/host.ts:40](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/host.ts#L40)

### `AppearanceHost`

Interface.

The appearance host. Only `prefs` is required.

```ts
interface AppearanceHost
```

**Properties**

- `boot?` (`AppearanceBoot`): Where the pre-paint snapshot goes. Absent: none is written.
- `prefs` (`AppearancePrefs`): The stored preferences.
- `window?` (`AppearanceWindow`): Native window effects. Absent: the window is left alone.
- `themes?` (`(folder: string) => Promise<readonly Theme[]>`): Themes beyond the built-in ones for a folder (its extensions'). Absent: built-in themes only.

Source: [ui/src/appearance/headless/host.ts:56](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/host.ts#L56)

### `AppearancePrefs`

Interface.

The stored preferences: read with `get`, follow with `subscribe`, change with `set`.

```ts
interface AppearancePrefs extends Readable<AppearanceState>
```

**Properties**

- `get` (`() => AppearanceState`, from `Readable`): Returns the current value.
- `isFirstRun` (`() => boolean`): True until preferences were stored: the app's first run on this computer.
- `reset` (`() => void`): Back to `DEFAULT_APPEARANCE`.
- `set` (`(update: Partial<AppearanceState> | ((prev: AppearanceState) => AppearanceState)) => void`): Merges `update` into the preferences (or replaces them with what the function returns), stores them and tells
  subscribers. Equal preferences change nothing.
- `subscribe` (`(onChange: () => void) => () => void`, from `Readable`): Registers a listener called after the value changes. Returns a function that removes the listener.

Source: [ui/src/appearance/headless/host.ts:19](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/host.ts#L19)

### `AppearanceState`

Interface.

Every appearance preference, as stored.

```ts
interface AppearanceState
```

**Properties**

- `accentOverride` (`string | null`): `#rrggbb` replacing the theme's accent, or `null` to use the theme's own.
- `codeTheme` (`CodeThemeChoice`): The code theme per appearance.
- `contrast` (`number`): 50..200, 100 = as designed.
- `darkTheme` (`string`): The theme id shown while dark (registry or extension).
- `filesTreeSide` (`FilesTreeSide`): Which side of the Files tab the tree column sits on.
- `fonts` (`FontChoices`): Font families per role.
- `fontSizes` (`FontSizes`): Font sizes per role.
- `interfaceScale` (`number`): The interface zoom factor, 0.8..1.5.
- `lightTheme` (`string`): The theme id shown while light (registry or extension).
- `mode` (`AppearanceMode`): System, light or dark.
- `motion` (`MotionPreference`): The motion preference.
- `panelMs` (`number`): Panel open/close duration in ms (sidebar, side panels), 0..400.
- `panelWidth` (`number`): The side panel's drag-resized width in CSS px; persisted on drag end only.
- `sidebarWidth` (`number`): The sidebar's drag-resized width in CSS px; persisted on drag end only.
- `springs` (`boolean`): Spring animations: motion settles with physics (a hint of overshoot)
  instead of fixed ease-out curves. Only meaningful at the Full level.
- `version` (`2`): The blob's version.

Source: [ui/src/appearance/headless/prefs.ts:60](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L60)

### `AppearanceWindow`

Interface.

What the window does natively when the appearance changes. Each member is optional.

```ts
interface AppearanceWindow
```

**Properties**

- `setBackground?` (`(color: string) => Promise<void>`): Paints the window background shown during resize, before the page does. `#rrggbb`.
- `setNativeTheme?` (`(mode: AppearanceMode) => Promise<void>`): Mirrors the mode into the native chrome (title bar, menus).
- `setZoom?` (`(factor: number) => Promise<void>`): Applies the interface scale (a zoom factor, 0.8..1.5).

Source: [ui/src/appearance/headless/host.ts:46](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/host.ts#L46)

### `BootSnapshot`

Interface.

The pre-paint snapshot: the last applied CSS variables, replayed before any script loads.

```ts
interface BootSnapshot
```

**Properties**

- `appearance` (`'dark' | 'light'`): The appearance class (`.light` / `.dark`).
- `vars` (`Record<string, string>`): `--zg-*` name → value.

Source: [ui/src/appearance/headless/host.ts:32](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/host.ts#L32)

### `CodeThemeChoice`

Interface.

Syntax colours per appearance. Only the tokens come from the code theme; the code
background is always the app's, so each slot holds a theme of its type.

```ts
interface CodeThemeChoice
```

**Properties**

- `dark` (`CodeThemePick`): The pick while the app is dark.
- `light` (`CodeThemePick`): The pick while the app is light.

Source: [ui/src/appearance/headless/prefs.ts:49](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L49)

### `FontChoices`

Interface.

The font families picked per role, as the OS names them. `null`: the stylesheet default.

```ts
interface FontChoices
```

**Properties**

- `chat` (`string | null`): The transcript font (default: the interface font).
- `mono` (`string | null`): The code font.
- `prompt` (`string | null`): The prompt font (default: the transcript font).
- `ui` (`string | null`): The interface font.

Source: [ui/src/appearance/headless/prefs.ts:18](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L18)

### `FontOption`

Interface.

A font the picker offers.

```ts
interface FontOption
```

**Properties**

- `family` (`string`): Family name as passed to CSS.
- `kind` (`'mono' | 'sans' | 'serif'`): Its kind, for grouping.
- `label` (`string`): Name shown in the picker.

Source: [ui/src/appearance/headless/fonts.ts:22](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L22)

### `FontSizes`

Interface.

Font sizes in CSS px.

```ts
interface FontSizes
```

**Properties**

- `chat` (`number`): The transcript.
- `code` (`number`): Code blocks and the editor.
- `prompt` (`number`): The prompt.

Source: [ui/src/appearance/headless/prefs.ts:30](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L30)

### `ParseAppearanceOptions`

Interface.

Options of [`parseAppearance`](https://zidane.si/docs/reference/ui-appearance-headless.md#parseAppearance).

```ts
interface ParseAppearanceOptions
```

**Properties**

- `codeThemeType?` (`(id: string) => 'dark' | 'light' | undefined`): The appearance a code theme id is made for, `undefined` when unknown
  (`codeThemeTypeOf` from `zidane-ui/appearance`). A stored pick of the
  wrong type, or unknown, reads as `'auto'`. Absent: any non-empty id is kept.

Source: [ui/src/appearance/headless/prefs.ts:129](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L129)

### `StorageAppearancePrefsOptions`

Interface.

Options of [`createStorageAppearancePrefs`](https://zidane.si/docs/reference/ui-appearance-headless.md#createStorageAppearancePrefs).

```ts
interface StorageAppearancePrefsOptions extends ParseAppearanceOptions
```

**Properties**

- `codeThemeType?` (`(id: string) => 'dark' | 'light' | undefined`, from `ParseAppearanceOptions`): The appearance a code theme id is made for, `undefined` when unknown
  (`codeThemeTypeOf` from `zidane-ui/appearance`). A stored pick of the
  wrong type, or unknown, reads as `'auto'`. Absent: any non-empty id is kept.
- `key?` (`string`): The key. Default: [`APPEARANCE_STORAGE_KEY`](https://zidane.si/docs/reference/ui-appearance-headless.md#APPEARANCE_STORAGE_KEY).
- `storage` (`AppearanceStorage`): Where the preferences live.
- `watch?` (`(key: string, onChange: () => void) => () => void`): Calls `onChange` when another window changed the stored preferences; returns the unsubscribe. Default: the
  `storage` event of `window`, when there is one.

Source: [ui/src/appearance/headless/storage.ts:20](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/storage.ts#L20)

## Types

### `AppearanceMode`

Type.

Which appearance the app shows: the OS's, or always light or dark.

```ts
type AppearanceMode = 'system' | 'light' | 'dark'
```

Source: [ui/src/appearance/headless/prefs.ts:12](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L12)

### `AppearanceStorage`

Type.

The `Storage` calls the appearance stores make (`localStorage` or a double).

```ts
type AppearanceStorage = Pick<Storage, 'getItem' | 'setItem'>
```

Source: [ui/src/appearance/headless/storage.ts:17](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/storage.ts#L17)

### `CodeThemePick`

Type.

A bundled code theme id, or `'auto'`: the app theme's own mapping.

```ts
type CodeThemePick = 'auto' | (string & {})
```

Source: [ui/src/appearance/headless/prefs.ts:43](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L43)

### `FilesTreeSide`

Type.

Which side of the Files tab the tree column sits on.

```ts
type FilesTreeSide = 'left' | 'right'
```

Source: [ui/src/appearance/headless/prefs.ts:40](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L40)

### `FontCssVars`

Type.

The font CSS variables [`fontsToCssVars`](https://zidane.si/docs/reference/ui-appearance-headless.md#fontsToCssVars) writes.

```ts
type FontCssVars = Record<'--zg-font-ui' | '--zg-font-chat' | '--zg-font-prompt' | '--zg-font-mono' | '--zg-font-size-chat' | '--zg-font-size-prompt' | '--zg-font-size-code', string>
```

Source: [ui/src/appearance/headless/fonts.ts:88](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/fonts.ts#L88)

### `MotionPreference`

Type.

How much the UI animates. `system` follows the OS reduced-motion preference.

```ts
type MotionPreference = 'system' | 'off' | 'reduced' | 'full'
```

Source: [ui/src/appearance/headless/prefs.ts:15](https://github.com/Tahul/zidane/blob/main/ui/src/appearance/headless/prefs.ts#L15)

## Re-exports

These names are documented on the entry point that owns them.

- [theme/themes](https://zidane.si/docs/reference/ui-theme-themes.md): [`Theme`](https://zidane.si/docs/reference/ui-theme-themes.md#Theme)
