Conventions
The rules that are true of vf-HTML and are in no schema — published verbatim from the engine, because three blind runs found them by getting them wrong.
Everything below is served by the engine itself, as the notes beside the Block
vocabulary. They are not a restatement of the schemas: the schemas document
themselves, and the Block reference prints them. These
are the things that are true and that no schema can say — the ones three blind
evaluation runs discovered by getting them wrong.
They are printed here verbatim rather than rewritten. A second wording of a rule is a second rule, and the two drift.
- Every parameter is written as an HTML attribute, so every value is a string.
- There are no comments. <!-- … --> is refused, and so are doctype, CDATA and processing instructions — a document is Blocks and nothing else. Say why you did something in <vf-brief>, which travels with the design, or name a node with vf-name.
- A Block flows inside its parent by default and becomes absolutely positioned as soon as x or y is set.
- Paint order is document order: later siblings draw on top. There is no z-index.
- Colours are #rgb or #rrggbb only — no rgb(), no colour names. The one place a colour carries opacity is a gradient stop, where "#000000@0.6" is allowed and "@0" is fully transparent; that is how a scrim over a photograph fades out instead of ending on a visible edge.
- Layout comes from <vf-stack>, whose parameter names are shortened from CSS: direction, align, justify, and values like "between" rather than "space-between".
- Use <vf-group> to assemble several Blocks into one thing that moves, rotates and clips as a unit; use <vf-stack> to distribute things along an axis. block.group builds one out of Blocks that already exist and rebases their coordinates for you, so grouping never moves the picture.
- Lock, visibility and name are reserved attributes — vf-locked, vf-hidden and vf-name — and they are deliberately not Block parameters, so no Block declares them and block.setParam will refuse them. Use node.setLocked, node.setHidden and node.setName instead.
- A locked node refuses every command except node.setLocked, and so does everything inside it — including deleting the group it sits in. The lock is somebody's decision, not a bug: you may unlock it, but do it in its own command and say so in the note. vf_get_doc marks every locked and hidden node, so you can see it before you try.
- A hidden node still exists, keeps its parameters and its place in paint order, and simply is not drawn. Hide a variation rather than deleting it when you might want it back.
- For a glow, set fade on a shape rather than lowering opacity — a translucent disc still has a visible edge.
- Shapes the vocabulary cannot draw — arrows, curves, icons, logos — come in as SVG: write the markup with vf_add_asset, then place it with <vf-image>. Call vf_list_assets to see what is already available.
- Photography, textures and brush lettering are source material and are never drawn by the engine. Make or fetch the picture outside it, then hand vf_add_asset the bytes as `base64` and place the result with <vf-image>; `from` only reaches files already inside the workspace.
- Text is set in one of two bundled families — family="sans" or family="serif" — and both cover Latin and simplified Chinese. There is no font name to pass: a family with no bundled face would resolve against whatever fonts the rendering machine has, and the glyph-coverage check refuses any character the bundled faces cannot draw rather than letting it fall back.
- For a column of Chinese reading top to bottom, set vertical="true" on <vf-text> — never rotate. A rotation turns the glyphs on their side, and it cannot make successive columns run right to left, which is half of what vertical setting is.
- Artwork that arrived baked onto its own ground — brushed lettering, a paper grain, an illustration on white — goes down with blend="multiply" so it composites onto the page instead of covering what is beneath it as a rectangle. multiply keeps the darker of the two, so it only erases a ground that is white: commission the asset on white, or leave blend alone.
- italic="true" sets Latin in a bundled italic face. There is no italic Chinese face to bundle, so a Chinese run asked to slant would be slanted by the browser rather than drawn — the synthesized-style check warns about that by name. Emphasise Chinese with a different weight or family instead.
- blur on <vf-image>, <vf-shape> or <vf-group> is depth of field — it softens the Block itself, and its edges with it, so oversize a blurred full-bleed image past the canvas and set bleed. backdrop-blur on a shape or group softens what is behind instead and leaves the Block sharp: that is the frosted panel, and it needs a translucent fill in front of it or the panel just covers what it blurred.
- Line breaks are controllable and do not need a binary search on max-width: forces a break, keeps two words together, ­ permits one inside a long word, and wrap="balance" evens out the lines of a heading.
- To lift a panel or card off the surface behind it, set shadow on <vf-shape> or <vf-group> — fade only makes a radial glow, which cannot do it for a rectangle.
The same list is what vf_list_blocks returns to an agent, and what the editor's
inspector renders its rows from. If you are reading this to write a document by
hand, read it once end to end: most of it is a sentence that saves a rejection.
Text and material
The two bundled families and why there is no font name, vertical setting for CJK, controlling line breaks, and how a photograph enters a document.
What gets refused
Every rejection code, the reserved attributes no Block declares, the budgets a document lives inside, and why refusals are structured.