LayoutActor

The default base class for renderable actors — canonical layout, transform, appearance and item groups, plus measured geometry.

LayoutActor extends Actor with the groups every renderable thing needs. Declaring one gives you all of them.

class Badge extends LayoutActor {
  label = signal("Ready");
}

Canonical groups

badge.layout.x(); // LayoutValue: number | pct(n) | "50%"
badge.layout.y();
badge.layout.width();
badge.layout.height();

badge.transform.scale();
badge.transform.rotation();

badge.appearance.opacity();

badge.flexItem.alignSelf();
badge.gridItem.gridColumn();
badge.gridItem.gridRow();

Change the defaults by overriding layout with layoutGroup:

class Image extends LayoutActor {
  layout = layoutGroup({ width: 320, height: 180 });
}

Animating

yield* badge.layout.x.tween(100, 0.4);
yield* badge.layout.y.tween(200, 0.4);

yield* this.runtime.all(
  badge.layout.x.tween(100, 0.4),
  badge.layout.y.tween(200, 0.4),
);

Authored input vs resolved output

This distinction causes more confusion than any other part of the class.

AuthoredResolved
Written byyouthe runtime
Acceptsnumbers, pct(n), "50%"
Always numericnoyes
Read whereanywhereresolution time
// Authored input — write here
actor.layout.x.set("50%");

// Resolved output — read only, always numeric
actor.localLayout(); // position relative to the parent
actor.worldLayout(); // position in world space
actor.bounds(); // currently aliases localLayout()

Percentages resolve against the containing block: the scene viewport for root actors, the nearest positioned ancestor's local box for nested ones. Renderers size their element from resolved localLayout.

Never author through localLayout

localLayout and worldLayout are outputs. Setting defaults or overrides on them is writing to the answer instead of the question.

measuredBox

LayoutActor includes a built-in measuredBox host binding: the renderer publishes the actor's actual rendered box, and the actor reads it back.

this.measuredBox(); // ResolvedLayoutBox | null

It is a recorded channel, so seeking backwards replays the measurement that was actually taken at that frame — and it is demand-gated, so nothing is measured unless something reads it.

On this page