Styles
How everything looks is data: a style. Classic and Futuristic are built in and written the same way anyone's own style is, so they are the worked examples (convex/lib/styles.ts). Every setting, its options and ranges: docs/STYLES.md. This page is how to use them.
The three places a look lives #
"look": { "style": "paper", "scheme": "system", "changes": { "colors": { "accent": "#15803d" } } },
"styles": [{ "id": "paper", "name": "Paper", "extends": "classic",
"type": { "body": "serif" }, "shape": { "radius": 20 } }],
"pages": [{ "id": "party", "title": "Party", "blocks": [], "look": { "backdrop": { "kind": "dots" } } }]
look.style: which style the space uses (classic,futuristicor an id fromstyles).look.changes: small tweaks on top, without making a style ("make the accent green").page.look: one page looks different (a party page with dots).
The ground behind everything is the style's backdrop: plain, grid, dots or gradient, with size and moving beams. The lit scene adds lines (1 to 4 colours the grid sweeps through), glow (0 to 3 drifting lights), floor (a lit floor of lines moving toward you) and streaks (0 to 3 light streaks). It's drawn by src/components/Scene.tsx from convex/lib/scene.ts, and the moving parts follow the Performance rules.
Later, a person's own look on their device sits on top of all of these.
Layers #
Lowest first, each the same shape, so any layer can change anything: platform default → the style and what it extends → look.changes → page.look → (later) the person's device look. A setting not given falls through to the layer below, so a style lists only what it changes.
Colours #
- Core names every screen uses:
bg surface raised text text2 text3 line grid accent accent2 onAccent success warn danger. - Any other name is yours:
gold,win,felt,wheel-1. Kits, blocks and mini-apps read them; a style that doesn't set one falls back safely. - Values:
#rrggbb,#rrggbbaa,white,black,transparent, another colour's name,mix(a, b, 40%),alpha(a, 20%),contrast(a). They nest, so three colours can derive the rest:
"schemes": { "dark": { "bg": "#0b1410", "text": "#e8f3ec", "accent": "#34d399" } },
"colors": { "surface": "mix(bg, text, 6%)", "line": "alpha(text, 12%)", "onAccent": "contrast(accent)" }
- Schemes:
light,darkor any name (night,contrast), up to 8.look.scheme: "system"follows the device.
Recipes #
| Ask | Change |
|---|---|
| "Make the accent orange" | add /look/changes/colors/accent "#ea580c" |
| "Rounder corners everywhere" | add /look/changes/shape/radius 22 |
| "Dark mode always" | replace /look/scheme "dark" |
| "Buttons outlined, not filled" | add /look/changes/parts/button { "treatment": "outline" } |
| "A calm look with no animation" | add /look/changes/motion { "speed": "off", "haptics": "off" } |
| "Warm paper with a serif font" | new style in /styles extending classic, then replace /look/style "paper" |
A path's parents must exist: if /look/changes or /look/changes/colors isn't there yet, add it first (add /look/changes { "colors": {} }), or add the whole object in one op.
Rules for anything you build #
- Never write a colour, font or size. Read the resolved style through
useWorkspace()and the helpers insrc/lib/workspace.tsx:surface(theme, part)for a box,bodyText(theme, size)andheadingText(theme, size)for text,gap(theme, steps)for spacing,color(theme, name)for a colour by name. - Need something new? Add a colour name or a part, not a hard-coded value.
- Parts a style can restyle one by one:
pane card button input bubbleMine bubbleTheirs tabbar header(treatment, depth, radius, bg, text, line). - Motion and haptics are settings. Respect
theme.motion.speed(offmeans no animation) andtheme.motion.haptics. Planned: the phone's Reduce Motion setting counts asoff. - Mini-apps get the style too as CSS variables and
window.zvessa.style(Mini-apps).
Safe by design #
Styles are checked on the server (checkStyles, checkLayer): unknown settings, values out of range and anything that isn't a colour value are refused. A style can't run code, load a URL or fake a screen. A colour that can't resolve falls back to Classic's, so a mistake never breaks a screen.