1
Open .vendo/theme.json
vendo init already wrote it, filled from your app’s own CSS. Five groups,
one file..vendo/theme.json
2
Adjust the tokens you care about
Edit values in place. Here a purple accent, tighter corners, and a compact
scale:Keep all five groups. The file is a whole theme, not a patch.
.vendo/theme.json
3
Every surface re-themes
The provider you pasted from init already imports the file, so there is
nothing else to wire.Save and reload. The overlay, slots, generated screens, approval modals, and
toasts each stamp these tokens on their own root and move together. Nothing in
the chrome hardcodes a Vendo brand.
app/layout.tsx
One surface, a different theme
The provider sets the brand for everything under it. A single surface can differ — a dark assistant panel on a light page, a compact slot in a dense console — with its owntheme prop.
VendoOverlay, VendoSlot, VendoTrigger,
VendoAppEmbed, VendoApprovalEmbed, and VendoToolResult. Type it with
Partial<VendoTheme>.
What a surface sends to document.body goes with it. The overlay panel, the
approval modal a press parks on, and the toast stack all wear the theme of the
surface they came from rather than falling back to the provider’s.
A slot showing your own markup renders it untouched, with no Vendo wrapper at
all. There is no chrome on screen there, so theme has nothing to style.
Good to know
- Extraction reads your root layout, the CSS that layout imports, and
tailwind.config.*, taking the shadcn and Tailwind names exactly:--primary,--background,--card,--font-sans,--radius. - Every group also lands as
--vendo-*CSS variables on the surface root, sovar(--vendo-density-card-padding)in your own styles tracks the same theme. --vendo-color-schemeis derived, not written. Vendo takes the relative luminance ofcolors.backgroundand flips todarkbelow0.179, so a dark brand gets dark chrome on its own.- Those five groups are the whole required shape. Every token added since is optional, so a theme file written a year ago still parses.
vendo syncre-reads your app and updates the slots you have not touched. A slot you edited by hand is pinned and reported, andvendo sync --theme-refreshtakes your app’s value back.createVendohas its ownthemekey, resolved once at boot. It feeds app generation and the one theme line in the agent’s prompt, not the chrome. Leave it unset and the server reads.vendo/theme.json.
Token reference
Every value the five groups expand into. Open a panel when you need an exact number.Density variables
Density variables
density: "compact" tightens control sizes, padding, and gaps, and
comfortable relaxes them. Every compact value is less than or equal to its
comfortable counterpart.A Kit container takes its own
density adjective and re-emits this same
ladder on its own element. That is how a compact table sits inside a
comfortable page.Color scheme
Color scheme
Vendo derives
color-scheme from the WCAG relative luminance of
colors.background and publishes it as --vendo-color-scheme. Backgrounds
below 0.179 resolve to dark, and everything else resolves to light.A color the parser cannot read resolves to
light. Only #rgb, #rgba,
#rrggbb, and #rrggbbaa are parsed.The chrome’s light-dark() branches key off this variable, so a dark-brand
host gets dark chrome with no extra configuration. Read it from your own
styles to make a custom surface track the same flip:Motion variables
Motion variables
motion: "reduced" collapses the duration to 0ms. The easing curve does not
change.Both defaults are overridable. Set
motionDuration or motionEasing on the
theme and full motion uses your values instead, while reduced still pins
the duration to 0ms.Mobile takeover
Mobile takeover
The overlay panel goes full-bleed at viewports 767px wide and narrower. It
covers the host layout rather than docking inside a squeezed column.The panel portals to Keep the
document.body, so a transformed or filtered host
ancestor cannot confine it. It pads all four edges with the iOS safe-area
insets.Touch ergonomics are keyed to small viewports or coarse pointers, so a
tablet in a wide orientation still gets honest targets. The composer renders
at 16px to stop iOS auto-zoom, and icon buttons and the overlay close control
expand to 44px.Desktop chrome is unchanged.While the takeover is open, the panel stamps a --fl-kb-inset variable
derived from visualViewport so the composer lifts above the on-screen
keyboard. Read it if you render a custom surface inside one:0px fallback. Outside the takeover the variable is not set at all,
and with the takeover open but no keyboard showing it is 0px.