Document shape
The regions a vf-HTML document is made of, where a canvas takes its size from, and what a node id is.
One root, one canvas
<vf-doc studio="core" spec-version="1">
<vf-canvas preset="APP_STORE_6_9" locale="en-US"></vf-canvas>
</vf-doc><vf-doc> is the root of every document and draws nothing. It declares which
Studio the document belongs to and which version of the format it is written in.
Inside it are two regions, of which only the first is drawn:
| Region | Drawn | What it holds |
|---|---|---|
<vf-canvas> | yes | The surface. Exactly one, always serialized first. |
<vf-brief> | no | Why this design is what it is: intent, audience, constraints. |
<vf-brief> is a sibling of the canvas rather than a child of it, because canvas
content is what gets drawn and the brief is not. It holds <vf-field> elements,
each a named piece of text — intent, audience, constraint and rationale
are the conventional names, and a Studio may expect its own.
The brief is not decoration. It is versioned and diffed like everything else and loaded into the assistant's context on every turn — so a design carries the judgement behind it, not only its result.
Where the canvas gets its size
A canvas takes either a preset or an explicit width and height, never
both. Prefer the preset wherever one exists: it carries the destination's exact
pixel size and its safe area, the region the platform guarantees will not be
cropped or covered by its own chrome. Writing the size by hand gives a canvas a
zero safe area, and then nothing remarks that the design is cropped where it
ships. It does still get a margin — padding="safe" falls back to a
proportional default when no preset supplies a figure — but a margin is not a
guarantee, and nothing checks a design against it.
Every preset, with its safe area →
Two canvas modes render today: fixed (an exact width and height) and flowing
(a fixed width, with the height being whatever the content needs and measured
after layout). paged is declared and refused — it needs the renderer's PDF
output.
Node ids
Every node in a validated document carries a vf-id, and it is minted at parse
rather than only when a command creates one. The shape is the Block's type minus
the vf- prefix, then a per-type counter: text-0, shape-0, text-1.
Three consequences, and each has cost somebody a debugging session:
- An id names a node, not a place. Inserting a Block does not renumber the ones after it. An id read in one turn is still valid many edits later.
- Structure is never derived from an id.
text-3has no parent you can compute and no type you can rely on — the prefix is there to be read, and a node may carry a name a person chose instead. Ask what created a node (vf_applyreturnscreated) or read the outline (vf_get_doc), which reports each node's parent and depth. - A literal id in client code is a bug. The canvas is whatever the tree says the canvas is; a parent is whatever the tree says the parent is.
vf-name is the human-readable layer name beside the id, and it is a reserved
attribute rather than a Block parameter — see
what gets refused for the whole reserved set and
what it means that no Block declares them.
Versions and migration
A document declares its format version (spec-version), and a migration module
upgrades older documents when they are read. Breaking changes to the format are
announced and migrated, not avoided. A document declaring a version this build
cannot read or migrate from is refused with unsupported-version rather than
being parsed optimistically.
The engine, in one page
VizFor designs are specifications, not pixels. This section publishes the specification format — the Blocks, the commands, the gate and the tools — for the people and the agents that write it.
Placement and layout
Flowing versus absolute, what anchor and bleed are for, why paint order is document order, and when to reach for a group instead of a stack.