Layout, text and tokens
Every page in Zvessa is built from the same small set of layout and text blocks. A block never holds a size, font or colour. It names a text role, a size step or a colour role, and the space's style says what those look like. Change a token once and every page follows.
The AI and MCP clients use these blocks through propose_change, like any other block. People add them by hand in the page editor (Page header, Section, Grid, Shelf, List rows, Space, Badge, Tags).
How a page is put together #
A calm page usually reads top to bottom like this:
- header: the page's title, with an optional small line over it, a line under it, a mark or logo, and one or two action buttons. When it is the first block it is the page title, and the bar at the top shows its title once you scroll (the iPhone large-title behaviour).
- section blocks, each with a small uppercase title and an optional "See all" link.
- Inside a section: a grid of cards or stats, a scroll shelf people swipe sideways, a row of buttons or badges, or a group of item rows.
- An item (or a section with
open: true) that holds blocks opens them as their own screen, so a settings page can live inside one page.
Defaults are quiet: no extra lines or frames unless the style asks for them, small uppercase labels over groups, one icon per row.
Example #
[
{ "id": "top", "kind": "header", "props": { "title": "Studio", "eyebrow": "Today", "subtitle": "Everything happening in your space", "mark": "S" },
"children": [{ "id": "top-search", "kind": "button", "props": { "icon": "search", "href": "#search" } }] },
{ "id": "glance", "kind": "section", "props": { "title": "At a glance", "action": "See all", "href": "#stats" }, "children": [
{ "id": "glance-grid", "kind": "grid", "props": { "columns": 2 }, "children": [
{ "id": "members", "kind": "stat", "props": { "label": "Members", "value": "128", "change": "12%", "trend": "up" } },
{ "id": "bookings", "kind": "stat", "props": { "label": "Bookings", "value": "24" } } ] } ] },
{ "id": "settings", "kind": "group", "props": { "title": "Your space" }, "children": [
{ "id": "people", "kind": "item", "props": { "title": "People & invites", "icon": "users", "href": "#people" } },
{ "id": "alerts", "kind": "item", "props": { "title": "Notifications", "icon": "bell", "value": "On", "href": "#alerts" } } ] }
]
Designing a header #
Everything about a header is a setting on the block:
| What | Setting | Options |
|---|---|---|
| Size | size | large (default, the big title), inline (a compact bar), hero (a tall band) |
| Height | height | compact, regular, tall |
| Background | background | none (default), surface, tint, gradient, glass (frosted), image |
| Title style | titleVariant | any text role below |
| Title font | font | body, display, mono (the style's) or a font name |
| Title weight | weight | 400, 500, 600, 700, 800 |
| Title colour | titleTone | a colour role or value, or gradient (accent to accent2) |
| Alignment | align | start, center |
| Logo | mark / image | a letter, emoji or icon name; or an https picture |
| Colour | tint | the mark and band colour |
| Line under it | divider | true or false |
| Actions | child button blocks | icon-only buttons when they have an icon and no label |
Text roles (tokens) #
Each role is a token in the style at type.roles.<role> with these settings: size (8-96 px, before type.scale), weight (400-800), font (body, display, mono or a font name), case (none or upper), tracking (-2 to 6 px), leading (1-2, line height as a multiple of size) and tone (a colour role). display, title and headline also follow the style's headingCase and tracking when they don't set their own.
Values below read: size · weight · font · case · tracking · leading · colour. A dash means the style's default.
| Role | Classic | Futuristic |
|---|---|---|
| display | 34 · 800 · display (system) · - · -0.4 · 1.15 · text | 34 · 800 · display (mono) · - · -0.4 · 1.15 · text |
| title | 28 · 700 · display (system) · - · -0.3 · 1.2 · text | 28 · 700 · display (mono) · - · -0.3 · 1.2 · text |
| headline | 22 · 700 · display (system) · - · - · 1.25 · text | 22 · 700 · display (mono) · - · - · 1.25 · text |
| subtitle | 17 · 600 · body (system) · - · - · 1.3 · text | 17 · 600 · body (system) · - · - · 1.3 · text |
| body | 15 · - · body (system) · - · - · 1.45 · text | 15 · - · body (system) · - · - · 1.45 · text |
| callout | 16 · - · body (system) · - · - · 1.45 · text2 | 16 · - · body (system) · - · - · 1.45 · text2 |
| caption | 13 · - · body (system) · - · - · 1.35 · text2 | 12.5 · - · body (system) · - · - · 1.35 · text3 |
| label | 12.5 · 600 · body (system) · upper · 0.6 · 1.3 · text3 | 10.5 · 500 · mono (mono) · upper · 1.3 · 1.3 · text3 |
| overline | 11 · 600 · body (system) · upper · 1.2 · 1.3 · text3 | 10.5 · 500 · mono (mono) · upper · 1.3 · 1.3 · text3 |
| number | 28 · 700 · display (system) · - · - · 1.1 · text | 28 · 500 · mono (mono) · - · - · 1.1 · text |
| quote | 17 · - · body (system) · - · - · 1.5 · text2 | 17 · - · body (system) · - · - · 1.5 · text2 |
| code | 13 · - · mono (mono) · - · - · 1.5 · text | 13 · - · mono (mono) · - · - · 1.5 · text |
To change one for a whole space, change the look, for example:
{ "op": "add", "path": "/look/changes/type/roles/title", "value": { "size": 32, "font": "serif" } }
A page's own look (/pages/N/look) can do the same for that page only.
Other tokens the blocks read #
| Token | Where | What it does |
|---|---|---|
type.body, type.display, type.mono | style | The three fonts roles point at |
type.scale | style | Scales every role (0.75-1.5) |
space.density | style | The spacing unit: compact 6, regular 8, roomy 10 px. Size steps are xs 0.5, sm 1, md 2, lg 3, xl 5 units |
shape.radius, shape.radiusSmall | style | Corners of cards, buttons, chips |
shape.marks | style | Lit corner marks on cards (Futuristic) |
surface.treatment, surface.depth | style | Fill, outline, glass or gradient; flat, shadow or glow |
parts.card, parts.header, parts.button, parts.tile, parts.chip | style | Restyle one part: treatment, depth, radius, bg, text, line |
| Colour roles | style | bg, surface, raised, text, text2, text3, line, accent, accent2, onAccent, success, warn, danger, gold and any name a style adds |
Cards #
| Size | Use it for |
|---|---|
sm | Small squares in grids and shelves |
md | The everyday card (default) |
lg | A big card with room for a story |
wide | Icon or picture on the left, words on the right; takes the full width of a grid |
hero | Tall, the picture fills it and the words sit on top; full width |
Variants: default (the style's card), outline, filled (a soft tint), accent (solid colour, or a lit edge in outlined styles), glass, plain (no frame).
Every layout and text block #
header #
The page's header (app bar), first on a page: a small line over the title (eyebrow), the title, a subtitle, an optional mark or logo, and its action buttons (children: button blocks, icon-only when they have an icon and no label). size 'large' is the phone's big title, 'inline' a compact bar title, 'hero' a tall picture band with the title on it. When it is the first block it replaces the page's own title, and the bar shows its title as you scroll.
| Setting | Type | What it does |
|---|---|---|
| title | string | Title. Empty uses the page's name |
| subtitle | string | A line under the title |
| eyebrow | string | A small line over the title (the space's name, a date, a section) |
| size | string | 'large' (default), 'inline' or 'hero' |
| align | string | 'start' (default) or 'center' |
| mark | string | A letter, emoji or icon name in a square before the title (like an app's logo) |
| image | string | https URL: the picture behind a 'hero' header, or the logo square otherwise |
| tint | string | Colour of the mark square or the hero band without a picture: a style colour role. Default accent |
| titleVariant | string | The title's text role (display, title, headline, subtitle, body, callout, caption, label, overline, number, quote, code). Default display (large), subtitle (inline), title (hero) |
| titleTone | string | The title's colour: a style colour role or a colour value. 'gradient' paints it from accent to accent2 |
| font | string | The title's font: 'body', 'display' or 'mono' (the style's), or a font name (system, rounded, serif, mono…) |
| weight | string | The title's weight: 400, 500, 600, 700 or 800 |
| background | string | Behind the header: 'none' (default, the page shows through), 'surface', 'tint' (a soft wash of tint), 'gradient' (tint to accent2), 'glass' (frosted, see-through) or 'image' (the picture in image) |
| height | string | 'compact', 'regular' (default) or 'tall' |
| divider | boolean | A thin line under the header |
Holds child blocks.
heading #
A title for a part of a page, with an optional small line over it and a line under it.
| Setting | Type | What it does |
|---|---|---|
| text | string, required | The words |
| level | number | 1 (title), 2 (headline), 3 (subtitle) or 4 (small label). Default 1 |
| subtitle | string | A quieter line under it |
| eyebrow | string | A small line over it |
| align | string | 'start' (default), 'center' or 'end' |
text #
Words in one of the style's text roles. variant picks the role: display, title, headline, subtitle, body, callout, caption, label, overline, number, quote, code. body is a paragraph, callout a bigger quiet intro, caption small print, label and overline small capitals, number a big figure, quote a pulled quote, code fixed-width.
| Setting | Type | What it does |
|---|---|---|
| text | string, required | The words. New lines are kept |
| variant | string | The text role: display, title, headline, subtitle, body, callout, caption, label, overline, number, quote, code. Default body |
| align | string | 'start' (default), 'center' or 'end' |
| tone | string | A colour role from the style (accent, accent2, success, warn, danger, gold, text2…). Default depends on the block. Default the role's own |
| lines | number | Show at most this many lines, then … |
list #
A list of short lines: bullets, numbers or ticks.
| Setting | Type | What it does |
|---|---|---|
| items | string[], required | One string per item |
| variant | string | 'bullet' (default), 'number' or 'check' |
badge #
A small pill with a word or a count: New, Live, 3, Beta, Sold out.
| Setting | Type | What it does |
|---|---|---|
| text | string, required | The words |
| tone | string | A colour role from the style (accent, accent2, success, warn, danger, gold, text2…). Default depends on the block. Default accent |
| icon | string | An icon name (home, chat, calendar, star, settings, users, bolt, game, coin…) or an emoji |
| variant | string | 'soft' (default: a light tint), 'solid' or 'outline' |
chips #
A row of small tags that wraps: topics, skills, opening days, filters to read.
| Setting | Type | What it does |
|---|---|---|
| items | string[], required | One tag per item |
| tone | string | A colour role from the style (accent, accent2, success, warn, danger, gold, text2…). Default depends on the block. Default neutral |
card #
A framed box: a picture or icon, a small line over the title, the title, a line under it, text, a badge, and child blocks. size 'sm' (a small square for grids and shelves), 'md' (default), 'lg' (big, a picture on top), 'wide' (picture or icon on the left, words on the right) or 'hero' (tall, the picture fills it with the words on top). With href the whole card opens it.
| Setting | Type | What it does |
|---|---|---|
| title | string | Title |
| text | string | Text |
| subtitle | string | A line under the title |
| eyebrow | string | A small line over the title |
| size | string | 'sm', 'md' (default), 'lg', 'wide' or 'hero' |
| variant | string | 'default' (the style's card), 'outline', 'filled' (a tint of tint), 'accent' (solid tint), 'glass' or 'plain' (no frame) |
| image | string | https URL of a picture |
| icon | string | An icon name (home, chat, calendar, star, settings, users, bolt, game, coin…) or an emoji |
| tint | string | Colour of the icon square and of 'filled' and 'accent' cards: A colour role from the style (accent, accent2, success, warn, danger, gold, text2…). Default depends on the block |
| badge | string | A small label in the corner (New, 2, Live) |
| href | string | '#page-id' opens a page, 'https://…' a website, 'chat:<conversation key>' a conversation (e.g. chat:support, chat:general), 'ai' the AI chat (people who manage), 'mailto:' or 'tel:' a mail or a phone call |
| action | string | Words of a button at the bottom that opens href (Book, Open, Play) |
Holds child blocks.
stat #
A big number with a label, and optionally its change (+12%) and a note.
| Setting | Type | What it does |
|---|---|---|
| label | string, required | What the number is |
| value | string, required | The number or short value |
| change | string | The change, as people should read it: +12%, -3, New |
| trend | string | 'up', 'down' or 'flat': colours the change (up green, down red) |
| note | string | A short line under it |
| icon | string | An icon name (home, chat, calendar, star, settings, users, bolt, game, coin…) or an emoji |
| href | string | '#page-id' opens a page, 'https://…' a website, 'chat:<conversation key>' a conversation (e.g. chat:support, chat:general), 'ai' the AI chat (people who manage), 'mailto:' or 'tel:' a mail or a phone call |
image #
A picture from a URL, in a shape you pick, with an optional caption.
| Setting | Type | What it does |
|---|---|---|
| src | string, required | https URL |
| alt | string | What the picture shows |
| ratio | string | Shape: 1:1, 4:3, 3:2, 16:9, 21:9, 3:4, 9:16. Default 16:9 |
| caption | string | Small words under it |
| round | boolean | A circle (a face, a logo) |
button #
A button that opens a page, a conversation, a link or the AI. With an icon and no label it is a round icon button (header actions).
| Setting | Type | What it does |
|---|---|---|
| label | string | Button words |
| href | string | '#page-id' opens a page, 'https://…' a website, 'chat:<conversation key>' a conversation (e.g. chat:support, chat:general), 'ai' the AI chat (people who manage), 'mailto:' or 'tel:' a mail or a phone call |
| variant | string | 'primary' (default), 'secondary', 'plain' or 'danger' |
| icon | string | An icon name (home, chat, calendar, star, settings, users, bolt, game, coin…) or an emoji |
| size | string | 'sm', 'md' (default) or 'lg' |
| full | boolean | Fill the width |
section #
A part of a page with a small title, an optional line under it and a link on the right (See all), holding child blocks. variant 'card' puts the children on one card. With open true it shows as one row that opens its children as their own screen (a settings page inside a page).
| Setting | Type | What it does |
|---|---|---|
| title | string | Small title over the section |
| subtitle | string | A line under the title |
| action | string | Words of the link on the right (See all, Edit) |
| href | string | Where the link goes: '#page-id' opens a page, 'https://…' a website, 'chat:<conversation key>' a conversation (e.g. chat:support, chat:general), 'ai' the AI chat (people who manage), 'mailto:' or 'tel:' a mail or a phone call |
| variant | string | 'plain' (default) or 'card' |
| open | boolean | Show as one row that opens the children as their own screen |
| icon | string | With open: the row's icon. An icon name (home, chat, calendar, star, settings, users, bolt, game, coin…) or an emoji |
Holds child blocks.
stack #
Lays out child blocks in a column (or a row).
| Setting | Type | What it does |
|---|---|---|
| direction | string | 'column' (default) or 'row' |
| gap | number | Space between children in px, 0-64, default 12 (row and grid take size steps instead) |
| align | string | 'stretch' (default), 'start', 'center' or 'end' |
Holds child blocks.
row #
Child blocks side by side, wrapping onto the next line when they don't fit: buttons, badges, small cards, stats.
| Setting | Type | What it does |
|---|---|---|
| gap | string | Space between: xs, sm, md, lg, xl. Default sm |
| align | string | Up and down: 'start', 'center' (default), 'end' or 'stretch' |
| justify | string | Across: 'start' (default), 'center', 'end' or 'between' |
| equal | boolean | Every child gets the same width (stats, cards) |
Holds child blocks.
grid #
Child blocks in columns of equal width: cards, stats, pictures. A child can span more columns with props.span.
| Setting | Type | What it does |
|---|---|---|
| columns | number | Columns on a phone, 1 to 6. Default 2 |
| columnsWide | number | Columns on a tablet or the web, 1 to 8. Default twice columns, up to 4 |
| gap | string | Space between: xs, sm, md, lg, xl. Default sm |
Holds child blocks.
scroll #
A shelf of child blocks people swipe sideways: cards, pictures, stats. Each child is as wide as its size (cards) or itemSize.
| Setting | Type | What it does |
|---|---|---|
| itemSize | string | Width of children that don't say: 'sm', 'md' (default), 'lg' or 'full' |
| gap | string | Space between: xs, sm, md, lg, xl. Default sm |
| snap | boolean | Stop on each child. Default true |
Holds child blocks.
group #
Grouped list rows on one card with thin lines between them (a settings list, a menu, a contact card). Its children are usually item blocks.
| Setting | Type | What it does |
|---|---|---|
| title | string | Small title over the group |
| footer | string | A small note under it |
| variant | string | 'inset' (default: on a card) or 'plain' (rows on the page) |
Holds child blocks.
item #
One list row: an icon or picture, a title, a line under it, a value on the right, a badge, and an arrow when it opens something. Opens href, or, when it holds child blocks, opens them as their own screen titled by the row.
| Setting | Type | What it does |
|---|---|---|
| title | string, required | Title |
| subtitle | string | A line under the title |
| icon | string | An icon name (home, chat, calendar, star, settings, users, bolt, game, coin…) or an emoji |
| tint | string | Colour of the icon square: A colour role from the style (accent, accent2, success, warn, danger, gold, text2…). Default depends on the block. Default accent |
| tone | string | How the icon square is painted: 'soft' (default), 'solid' or 'glossy' |
| image | string | https URL of a round picture in place of the icon (a face, a logo) |
| value | string | Short words on the right (On, 3 members, $40) |
| badge | string | A small pill on the right (New, 2) |
| href | string | '#page-id' opens a page, 'https://…' a website, 'chat:<conversation key>' a conversation (e.g. chat:support, chat:general), 'ai' the AI chat (people who manage), 'mailto:' or 'tel:' a mail or a phone call |
| danger | boolean | Red title (Delete, Leave) |
| chevron | boolean | The arrow on the right. Default: shown when it opens something |
Holds child blocks.
divider #
A thin line between parts, optionally with a word in the middle.
| Setting | Type | What it does |
|---|---|---|
| label | string | A word in the middle (or, Today) |
| inset | boolean | Start the line after the space of an icon, like between list rows |
spacer #
Empty space between parts.
| Setting | Type | What it does |
|---|---|---|
| size | string | xs, sm, md, lg, xl. Default md |