Example: the free-form text box¶
No manifest row · built today as betta-text-box
A fixed-size yellow panel with brand-blue copy centred inside it, wrapping to the panel's width. The docx describes it as "a free form text box the user can type into directly": the author places and sizes the box once, and whatever gets typed wraps inside it — a longer line wraps rather than resizing the panel. It wipes on from the left.
flowchart TB
subgraph scene["scene-text-box"]
BOX["text-box<br/>text · absolute · fixed box 417 × 278<br/>wipe from left"]
end
The document¶
{
"id": "scene-text-box",
"durationInFrames": 75,
"layers": [
// ── One layer. Both axes are numbers, so this is the FIXED-BOX text shape: wrap to
// width, then shrink to fit height if the copy still overflows. The panel fills the
// authored box; padding insets the copy from its edges.
{
"id": "text-box",
"type": "text",
"zIndex": 0,
"layout": {
"mode": "absolute",
"16:9": { "x": 120, "y": 300, "w": 417, "h": 278, "anchor": "top-left" },
"9:16": { "x": 120, "y": 600, "w": 417, "h": 278, "anchor": "top-left" },
"1:1": { "x": 120, "y": 300, "w": 417, "h": 278, "anchor": "top-left" }
},
"typography": {
"16:9": { "family": "GT Eesti Display", "weight": 700, "size": 60, "color": "#054F8E",
"lineHeight": 1.05, "multiline": true,
"horizontalAlign": "center", "verticalAlign": "middle" }
// "9:16": …, "1:1": …
},
"props": {
"content": [ { "value": "Your text here" } ],
"background": { "fill": "#FFD100", "padding": { "top": 24, "right": 32, "bottom": 24, "left": 32 }, "radius": 8 },
"overflow": "shrink" // only meaningful in this shape
},
"animations": [
{ "animation": "wipe", "params": { "from": "left", "durationInFrames": 12 },
"at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
]
}
// The same component as the AUTO-HEIGHT shape — "grow with what I type" instead of
// "shrink into the box I drew" — is one field: "h": "content". Then the panel is exactly
// as tall as the wrapped copy plus padding, and `overflow` has nothing to act on.
]
}
| Bound field | Control |
|---|---|
text-box.props.content[0].value |
text (multiline) |
text-box.typography.*.color, text-box.props.background.fill |
colour |
text-box.layout.*.w, text-box.layout.*.h |
number |
What the engine does with it¶
Resolve. One wipe expansion. Typography is already absolute.
Solve. Width is a number, so measure runs with a constraint: 417 − 32 − 32 = 353 px of line
width. Your text here fits one line; a long paragraph wraps to several. Height is also a number,
so if the wrapped block is taller than 278 − 24 − 24 = 230, the wrap-then-shrink solver scales the
type down until it fits (Text Foundation → the three shapes).
The box is 417 × 278 regardless of the copy.
Animate. The wipe is a clip-reveal, not a transform, so it rides the node's own paint; the box itself doesn't move.
Place. One element: the panel fills the box, the wrapped, possibly shrunk lines sit centred inside the padded area.
What this shows¶
- Three shapes, one type.
content/contenthugs,number/contentgrows,number/numberwraps then shrinks — this page is the third; fliphto"content"for the second. Nothing about the layer type changes. - "Types into it directly" means the box is the constraint. The author's drawn box is the contract; the copy adapts to it, never the other way round.
Stress points¶
- Padding is a layout input in the fixed-box shape, and the brief says it never is.
Text Foundation → Background padding is paint-only
states padding "must never influence the text's own position, contribute to its box, or
trigger a resize/rescale". For a point-text pill that's right: the pill grows outward from the
ink. For a fixed box it's backwards: the panel is the authored box, and padding must
inset the wrap width and the shrink height, or the copy runs to the fill's edges — exactly
what the built component does (
box-sizing: border-box, padding inside a 100%-sized panel). The rule needs a per-axis statement: on a"content"axis padding is a paint-only outset from the ink; on a number axis it is an inset that reduces the measure constraint. The "safe to enforce" argument (fit-to-box based on ink alone) only holds for the outset case. backgroundfills the box in this shape, hugs the ink in the others — unstated. Today's schema comment says so forpointTextvsboxText; under the collapsedtexttype the brief never says which the pill does whenwis a number. It must fill the authored box here.overflow's value set isn't in the brief. Text Foundation says the enum exists and applies only to the fixed box; the values aren't listed. Written as"shrink"here.- A layer-level
overflowcollides in name with the containeroverflow: show | hide(Positioning → flowBox vocabulary). Same word, two different value sets, two different meanings. One of them should be renamed (fitfor text, say).