HTML layers in doc.json
The html overlay clip: its fields, the rules the page enforces, and a live layer worked through.
An HTML layer is an overlay clip of kind html. Its source travels in the
document, so it is sharp on the cloud, in the studio and in every export,
never re-hosted and never counted as an upload.
{
"id": "callout",
"kind": "html",
"start": 2,
"duration": 4,
"html": "<div class=\"c\">Open Code</div>",
"css": ".c{font-family:'Inter';background:#0b0b0d;color:#fff;padding:20px;border-radius:16px;box-shadow:0 24px 60px rgba(0,0,0,.45)}",
"box": { "width": 452, "height": 132 },
"transform": { "x": 0.7, "y": 0.5, "scale": 1, "rotation": 0 }
}Fields
boxis the design box in 1080p pixels (the frame is 1920 wide).width, when set, is a fraction of the frame width; absent, the design size is used.bleedis the room around the box for what paints outside it, a shadow or a glow. It is derived from the shadows in the CSS when absent.fontslists a face outside the hosted catalog: an https URL, or a.woff2beside the document, whichvos pushuploads (0.64+). A hosted family needs no entry: name it in the CSS.animandmotionwork as on every layer.live: truere-rasterizes the layer per frame;dataholds the values{{data.name}}fills. See Live layers.
The three shapes, from the product's register
A callout is unmistakably the video's and still the product's when it
shares the product's hue and type and contrasts in value, elevation, scale
and place. Three shapes carry that, so a viewer learns them once: a note (a
kicker, a title and a line), a tag (one label on the accent) and a code
block (the payload). vos callout composes them from the product's own
register:
vos brand https://tweakcn.com --out take/BRAND.md
vos callout take note --step copy-click --kicker "index.css" \
--title "Every token you tuned, as CSS variables." --mark ring --leaderThe register is BRAND.md beside the take (bgA is the ground, accent,
fontBody) or --ground #hex --accent #hex --font "…" --body-px 14. The
card inverts the app's value in the product's hue (a light app gets a dark
card, a dark app a light one), keeps the accent for the kicker, sets its
title at 1.35 times the app's body as the camera shows it at the layer's
start, lifts on a real shadow and a hairline, rises in and fades out. Its
window opens a beat after the step's press and closes before the next
scroll or navigation, and it is pinned to the step. --at <s> --seconds <n>
places one without a step; --print prints the clip instead of writing
it.
vos validate take --picture renders the footage under every html layer at
its start and reports the colour difference between the card's ground and
what it covers: a card within a small difference of the app reads as one
more panel, and the validator says so. An unpinned layer whose window holds
a step with an element gets the pin named in a warning.
Pinning a layer to what it is about
A callout has a referent. Name it and the layer stays with it:
{
"id": "callout",
"kind": "html",
"start": 2,
"duration": 4,
"html": "<div class=\"c\">Copies the theme as CSS variables</div>",
"css": ".c{font-family:'Inter';background:#0b0b0d;color:#fff;padding:20px;border-radius:16px}",
"box": { "width": 452, "height": 96 },
"transform": { "x": 0.7, "y": 0.5, "scale": 1, "rotation": 0 },
"pin": { "step": "copy", "mark": "ring", "leader": true }
}pin names the referent by one of three things: step, a recorder step's
id from actions.json (hover, click, type and drag steps carry the
element's rect); press, a source second, for a human recording that has
no script (the nearest press names its element); or rect, fractions of
the video frame. The layer is placed a gap off one side of the referent
(side is auto, the first of right, left, below and above with room
inside the frame through the layer's life, or a side by name; gap is
design pixels) and carried with the referent as the camera zooms and pans.
The layer keeps its screen size; only its place follows. mark draws a
ring around the referent or an underline beneath it for the layer's
life, leader a hairline from the layer to the referent, color their
ink. transform stays the fallback for a pin that cannot resolve, and
vos validate says why: an unknown step, a step that touched nothing, a
scroll inside the layer's window, a frame with no room beside the referent.
In the studio a pinned layer says what it is pinned to in its Layout group;
dragging it, or pressing Unpin, gives its place back to the fields.
The rules the page enforces
- The markup is XML, not HTML:
<br />closes itself,&is written out, attribute values are quoted. Malformed markup decodes to nothing, sovos validaterefuses it in words before it ships. - The layer fetches nothing on its own. An image the source names on
assets.vos.so(the platform's own catalogs) is inlined the way the faces are; an image at any other address paints blank, a file of your own on vos.so included, so inline it as adata:URI. The validator names the host. - The CSS is the chrome.
shadow,radius,border,frameandkeyare refused on an html clip. - A still layer's CSS
animationruns on the wall clock, and the validator warns; make the layer live instead.
A live layer
{
"id": "progress",
"kind": "html",
"start": 2,
"duration": 4,
"live": true,
"data": { "label": "Rendering" },
"html": "<div class=\"w\"><span>{{data.label}}</span><span>{{t}} s</span><div class=\"bar\"><i></i></div></div>",
"css": ".bar{height:10px;background:#222}.bar i{display:block;height:100%;width:0;background:#4ade80;animation:fill 4s linear forwards}@keyframes fill{from{width:0}to{width:100%}}",
"box": { "width": 520, "height": 110 },
"transform": { "x": 0.5, "y": 0.5, "scale": 1, "rotation": 0 }
}{{t}} is the raw number of seconds, rounded to three decimals, with
trailing zeros dropped: it reads 2, then 2.6, then 2.633, and a bare
1 on every whole second. For a readout that holds its shape, build the
digits from --vos-t, which the page writes on a live layer, and give it
font-variant-numeric: tabular-nums:
.n::after{
counter-reset:
o round(down, var(--vos-t, 0), 1)
d calc(round(down, calc(var(--vos-t, 0) * 10), 1)
- round(down, var(--vos-t, 0), 1) * 10);
content: counter(o) '.' counter(d) ' s';
}Keep the , 0 fallback, or the declaration is invalid wherever the layer is
not live.
vos validate runs the layer's gate and says in words what would not paint.
To look at one moment: vos frames take --at 3.