> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vendo.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Theming Vendo surfaces with the VendoTheme token shape

> The VendoTheme token shape extracted into .vendo/theme.json and consumed by every shipped chrome surface, including colors, radii, density, and motion.

Init extracts `.vendo/theme.json`. The theme has this exact shape:

```ts theme={null}
export interface VendoTheme {
  colors: { background: string; surface: string; text: string; muted: string; accent: string; accentText: string; danger: string; border: string };
  typography: { fontFamily: string; headingFamily?: string; baseSize: string };
  radius: { small: string; medium: string; large: string };
  density: "compact" | "comfortable";
  motion: "full" | "reduced";
}
```

Init wires this file into the scaffolded `<VendoRoot theme={...}>` at build time
(sync regenerates it on each build), so a fresh install renders on brand without
passing `theme` by hand.

Pass partial runtime overrides through `VendoProvider.theme`. Shipped chrome
derives its colors, typography, radius, density, and motion from these tokens.
It does not hardcode a Vendo brand.

Generation receives the same theme. Optional `.vendo/design-rules.md` adds
generation-time design rules without changing runtime tokens.

## Emitted CSS variables

`VendoRoot` emits stable CSS custom properties derived from `density` and
`motion`. Chrome and the [prewired primitives](/connect/host-components) read
them directly, and you can consume the same variables from your own styles.
The `--vendo-density-*` and `--vendo-motion-*` names below are stable — new
variables may be added under these prefixes, but the ones listed here will
not be renamed.

### Density

`density: "compact"` tightens control sizes, padding, and gaps; `density:
"comfortable"` relaxes them. Every compact value is less than or equal to
its comfortable counterpart.

| Variable                          | `compact`  | `comfortable` |
| --------------------------------- | ---------- | ------------- |
| `--vendo-density-control-height`  | `32px`     | `38px`        |
| `--vendo-density-control-padding` | `6px 10px` | `9px 12px`    |
| `--vendo-density-card-padding`    | `12px`     | `16px`        |
| `--vendo-density-content-gap`     | `7px`      | `10px`        |
| `--vendo-density-inline-gap`      | `5px`      | `7px`         |
| `--vendo-density-field-gap`       | `4px`      | `6px`         |
| `--vendo-density-table-padding`   | `7px 10px` | `10px 12px`   |
| `--vendo-density-badge-height`    | `20px`     | `24px`        |
| `--vendo-density-badge-padding`   | `3px 7px`  | `5px 9px`     |
| `--vendo-density-stat-padding`    | `9px 11px` | `12px 14px`   |
| `--vendo-density-tabs-padding`    | `3px`      | `4px`         |
| `--vendo-density-tab-height`      | `26px`     | `30px`        |
| `--vendo-density-tab-padding`     | `4px 8px`  | `6px 10px`    |

### Color scheme

`VendoRoot` derives `color-scheme` from the WCAG relative luminance of
`colors.background` and exposes it as `--vendo-color-scheme`. Backgrounds
below the flip point (L \< 0.179) resolve to `dark`; everything else, including
unparseable values, resolves to `light`.

| Variable               | Light background | Dark background |
| ---------------------- | ---------------- | --------------- |
| `--vendo-color-scheme` | `light`          | `dark`          |

Chrome uses this variable to activate `light-dark()` branches, so a dark-brand
host automatically gets the dark chrome treatment without any extra
configuration. Set `colors.background` in `.vendo/theme.json` (or through a
`VendoProvider.theme` override) and the scheme follows.

```json .vendo/theme.json theme={null}
{
  "colors": {
    "background": "#14151a",
    "surface": "#1b1c22",
    "text": "#f5f5f7"
  }
}
```

Reference `--vendo-color-scheme` from your own styles if you want custom
surfaces to track the same flip:

```css theme={null}
.my-card {
  color-scheme: var(--vendo-color-scheme);
}
```

### Motion

`motion: "reduced"` collapses `--vendo-motion-duration` to `0ms` so chrome
respects user reduced-motion preferences. The easing curve stays constant.

| Variable                  | `full`                           | `reduced`                        |
| ------------------------- | -------------------------------- | -------------------------------- |
| `--vendo-motion-duration` | `160ms`                          | `0ms`                            |
| `--vendo-motion-easing`   | `cubic-bezier(0.2, 0.8, 0.2, 1)` | `cubic-bezier(0.2, 0.8, 0.2, 1)` |

Reference the variables from your own CSS to keep custom surfaces in sync
with the current theme:

```css theme={null}
.my-card {
  padding: var(--vendo-density-card-padding);
  transition: background-color var(--vendo-motion-duration) var(--vendo-motion-easing);
}
```

## Mobile takeover

Shipped overlay, page, and palette chrome switch to a full-viewport takeover
at viewports 767px wide and narrower. The takeover covers the host layout
instead of docking inside a squeezed column and portals to `document.body`
so transformed host ancestors cannot confine it. It honors iOS safe-area
insets on all four edges. On touch devices (`pointer: coarse`), the composer
and palette search render at 16px to prevent iOS auto-zoom, and icon buttons
and the overlay close control expand to a 44px touch target. Desktop chrome
is unchanged.

The takeover surface stamps a stable `--fl-kb-inset` CSS variable derived
from `visualViewport` so the composer lifts above the on-screen keyboard.
Read it from your own styles if you render a custom surface inside a
takeover:

```css theme={null}
.my-mobile-footer {
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--fl-kb-inset, 0px));
}
```

`--fl-kb-inset` is `0px` when no keyboard is open and outside of takeover mode.
