Docs

Layout

Do not shadow Node methods, do not flex-size to unmeasured text, match anchors to coordinates, and keep sizes proportional to the frame.

View as Markdown

Layout uses Flexbox on a hidden DOM tree. Text size is measured before the webfont loads, and offscreen before the node joins a document. Most broken custom elements come from that.

Flex is for placement inside a container that already has an explicit width and height. It is not for measuring content.

Do not shadow Node members

Never name a class field after a Node method or property. The field overwrites the real member. The element compiles and then draws nothing, or crashes at render.

Do not use these names for @signal fields:

draw, render, size, position, scale, opacity, cache, key, children, add, remove, view, parent, clip, filters, x, y, width, height, fill, stroke, rotation, zIndex

Prefix intent: drawProgress, revealProgress, textSize.

Do not flex-size to text you drew

A Rect that hugs a Txt is measured with a fallback font. After the real font loads, the label clips.

Compute width from the string instead:

const CHARACTER_WIDTH_RATIO = 0.55; // ~0.62 for monospace
const textWidth = this.label().length * this.textSize() * CHARACTER_WIDTH_RATIO;
const capWidth = Math.max(this.capHeight(), textWidth + this.textSize());

Give that number to the Rect as width. Then place children with layout inside that fixed box.

Set an explicit width on wrapping text and use textWrap. Do not rely on a manual newline inside Txt.

Anchors and coordinates

Vidova uses a center origin. X increases to the right. Y increases downward. Do not treat the origin as top-left.

offsetX of -1 means x is the left edge. Passing a center-based x then shifts the node by half its width. Derive both from one helper: an anchor of -1, 0, or 1, and the matching coordinate.

Sizes follow the frame

Hard-coded pixel margins that look right at 1920x1080 are wrong at the 480x270 thumbnail. Express insets as a fraction of the frame.

Hidden means hidden in every dimension. Setting visible to false is not enough if a stroked rect still draws. Zero opacity or size as well.

Code letter spacing

Code reads letter spacing from computed CSS, not from its signal. That CSS is normal until the element joins the document. Set letterSpacing to 0 on every Code node.

Code text uses fontFamily="Fira Code Variable". Do not pass a CSS font stack. The family string must match a bundled font exactly. UI text uses Inter Variable.

layout on a root

layout on a container with explicit width and height is safe. layout where the container size falls out of its children is where measurement bites.

<Rect layout width={480} height={72} gap={16} alignItems="center">
  <Circle width={24} height={24} />
  <Txt text={() => this.label()} fontSize={28} fontFamily="Inter Variable" />
</Rect>

The Rect has a fixed size. The Txt does not decide the box.

layout={false} overlays; it does not stack

Children with layout={false} do not occupy flex space. They overlay siblings. The parent size ignores them.

Use layout={false} only for overlay animation: a measured in-flow sibling sets the box, and a second copy with layout={false} moves or fades on top of it.

Stacked cards, counters, labels, and image-plus-chrome must stay in the layout flow with explicit width and height. If you mark the visible stack layout={false}, it draws on top of the previous child and the parent collapses.

<Rect layout width={480} height={220} direction="column" gap={12} alignItems="center">
  <Rect width={480} height={160} fill="#111" />
  <Txt text={() => this.label()} fontSize={28} fontFamily="Inter Variable" />
</Rect>

Both children participate in flex. The photo and the caption take height.

Signal bindings for labels: Signals. Intro vs timeline playback: Animation. Asset images inside a stack: Inputs.