Interface StackNode

Linear layout container - lays its children sequentially along direction (row = left -> right, column = top -> bottom) with gap between them.

Sizing:

  • If width/height is set on the relevant axis, that size is used.
  • If omitted on the flow axis, the stack hugs the sum of children sizes (including their margin) + gaps on that axis.
  • If omitted on the cross axis, the stack hugs the maximum of children sizes (including their margin) on that axis.
  • The fractional-Length rule applies per-axis: a child's fractional width/height on an auto-sized axis does not contribute to the stack's hug there (it would be circular) and resolves against the stack's final size at layout; the hug comes from "Npx"/intrinsic children.

Positioning of stacked children (unified with GroupNode's slot model - every child sits in a slot in the parent's inner area):

  • The stack flow distribution gives each child a slot whose flow extent matches its allocation and whose cross extent is the stack's full inner cross span. Inside that slot, size and position follow the same rules as for a group child: insets, explicit width/height, centerX/centerY, and min/max clamping. The only stack-specific extras are align (default cross positioning when no centre/inset is set) and the autofill rule on the cross axis (see below).
  • Size on each axis, in priority order:
    1. Dual inset on the axis -> fillSize - a - b (fillSize = slot size minus the child's margin on the axis).
    2. Explicit width/height -> as declared.
    3. Single inset on the axis with autofill -> fillSize - inset.
    4. Autofill (no inset, no explicit) -> fillSize.
    5. Intrinsic. Autofill applies to: rect children always; non-rect children only when nothing pins the axis - no inset and no centerX/centerY on it (on the cross axis align: stretch is additionally required). A single inset anchors a non-rect at its intrinsic size - exactly as in a group - while a rect still autofills from the anchor. min/max constraints do not change the regime - they clamp the autofilled size like any other computed size. So a rect with no height and centerY: 0 autofills the cross slot (and centerY: 0 is moot since the fill already centres it); the same rect with height: "8px" and centerY: 0 is 8px tall and centred.
  • Position on each axis, in priority order:
    1. Leading inset (left/top) -> fillStart + inset.
    2. Trailing inset alone -> fillEnd - inset - size.
    3. centerX/centerY -> fillStart + (fillSize - size)/2 + value.
    4. Cross axis with no positional field -> align.
    5. Otherwise -> fillStart. Insets and centerX/centerY resolve against the slot's size (both the anchor and the fraction's multiplier live in the slot); explicit dims and min/max resolve against the stack's inner size.
  • Negative single inset on rect autofill: left: "-4px" with no width autofills fillSize - (-4) = fillSize + 4 and anchors at fillStart + (-4) - the rect extends 4px past the leading slot edge while still reaching the trailing edge. Same idea on any axis. If a single inset places the child fully outside the slot, autofill collapses to size 0.
  • align still controls cross positioning for children with no cross-axis positional field. stretch (the default) is the autofill trigger for non-rect children. Per-child align overrides are not supported.
  • Positional tweens target def[prop] so they inherit the same rules. A { property: "centerY", from: -1 } on a row-stack child animates the cross centre offset (slot-relative). A { property: "top", from: "30px" } on a row-stack child shifts the autofill range / single-inset anchor over time.
interface StackNode {
    align?: "center" | "end" | "start" | "stretch";
    alpha?: number;
    bottom?: Length;
    centerX?: Length;
    centerY?: Length;
    children: Node[];
    direction: "row" | "column";
    gap?: `${number}px`;
    height?: Length;
    id?: string;
    left?: Length;
    margin?: Padding;
    mask?: string;
    maxHeight?: Length;
    maxWidth?: Length;
    minHeight?: Length;
    minWidth?: Length;
    right?: Length;
    top?: Length;
    type: "stack";
    visible?: boolean;
    width?: Length;
}

Hierarchy (view full)

Properties

align?: "center" | "end" | "start" | "stretch"

Cross-axis alignment of every child within its slot. Default "stretch".

alpha?: number

0..1, default 1.

bottom?: Length

Distance from parent's bottom edge to this node's bottom edge.

centerX?: Length

Horizontal offset from the centre of the parent. 0 = perfectly centred.

centerY?: Length

Vertical offset from the centre of the parent. 0 = perfectly centred.

children: Node[]
direction: "row" | "column"
gap?: `${number}px`

Space between adjacent children along the flow axis. Default 0.

height?: Length
id?: string

Required if the node needs to be targeted by animations or runtime setters. "frame" is reserved - used by positional tweens as relativeTo: "frame" to reference the video frame.

left?: Length

Distance from parent's left edge to this node's left edge.

margin?: Padding

Reserves space on each side of the node by inflating its bounding box. Lives on the child rather than as container-level padding, so the parent needs no declaration to give one of its children breathing room.

  • In an auto-sized parent the parent hugs child + margin, so margin effectively enlarges the parent. This drives the "background plate" pattern: in an auto-sized group, put an omitted-size Rect next to a Text with margin: "10px" -- the group hugs text plus margin, the rect autofills the group, and the text sits visually inset 10 px inside the plate.
  • In an explicit-sized parent the parent's size is fixed, so margin only shifts this node's own placement inward.
  • In a StackNode margin is added to the child's flow slot on both sides and compounds with gap: two adjacent children with margin: "10px" separated by gap: "5px" produce a 25 px visual gap (10 + 5 + 10).
mask?: string

Id of a RectNode with isMask: true whose bounds clip this node. The mask rect may live anywhere in the tree; it is resolved by id at parse time. Only rects may be used as masks.

maxHeight?: Length
maxWidth?: Length
minHeight?: Length

Lower bound for the node's computed height. See minWidth.

minWidth?: Length

Lower bound for the node's computed width. Clamps the laid-out size from below - applies to both auto-sized nodes (hugged size lifted to minWidth) and explicit-sized nodes (if width < minWidth, wins minWidth, same as CSS).

right?: Length

Distance from parent's right edge to this node's right edge.

top?: Length

Distance from parent's top edge to this node's top edge.

type: "stack"
visible?: boolean

Default true.

width?: Length