Example: Bonus Cashback, red flash¶
Manifest row 07 · built today as betta-bonus-cashback-flash
Bare yellow BONUS with no panel, then a red panel tipped 3° carrying a white UP TO stack beside
a big white $300, then bare white Cashback, then a footnote. No shadows. Everything sits on a
coloured scene background, which is why two of the three rows have no pill of their own. Rows wipe on
from the left four frames apart; the three rows breathe on a shared pulse; the footnote slides down
and fades in.
flowchart TB
subgraph scene["scene-cashback (one motion graph per ratio)"]
ROOT["flash-root<br/>flowBox · column · absolute (120,120)"]
PANELS["flash-rows<br/>flowBox · column · flow · pulse"]
BONUS["bonus · bare text · flow · wipe"]
AMT["amount-row<br/>flowBox · row · flow · wipe"]
PRE["amount-prefix · text 'UP TO' · flow"]
HERO["amount-hero · text price run · flow"]
AMTBG["amount-bg · background · derived"]
CASH["cashback · bare text · flow · wipe"]
FN["footnote · text · flow · slideFade"]
PANELS -- flowIn --> ROOT
FN -- flowIn --> ROOT
BONUS -- flowIn --> PANELS
AMT -- flowIn --> PANELS
CASH -- flowIn --> PANELS
PRE -- flowIn --> AMT
HERO -- flowIn --> AMT
AMTBG -- of --> AMT
end
The document¶
{
"id": "scene-cashback",
"durationInFrames": 75,
"layers": [
{
"id": "flash-root",
"type": "flowBox",
"zIndex": 0,
"layout": {
"mode": "absolute",
"16:9": { "x": 120, "y": 120, "w": "content", "h": "content", "anchor": "top-left" },
"9:16": { /* own position */ },
"1:1": { /* own position */ }
},
"props": { "direction": "column", "alignAcross": "centre", "gap": 46 }
},
// ── The three pulsing rows.
{
"id": "flash-rows",
"type": "flowBox",
"zIndex": 0,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "flash-root", "w": "content", "h": "content" },
"9:16": { "flowIn": "flash-root", "w": "content", "h": "content" },
"1:1": { "flowIn": "flash-root", "w": "content", "h": "content" }
},
"props": { "direction": "column", "alignAcross": "centre", "gap": 34 },
"animations": [
{ "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40, "delay": 30 } }
]
},
// ── Bare text, no pill: the scene background is the panel.
{
"id": "bonus",
"type": "text",
"zIndex": 2,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "flash-rows", "w": "content", "h": "content" },
"9:16": { "flowIn": "flash-rows", "w": "content", "h": "content" },
"1:1": { "flowIn": "flash-rows", "w": "content", "h": "content" }
},
"typography": {
"16:9": { "family": "GT Eesti Display", "weight": 700, "size": 200, "color": "#ffd100", "case": "uppercase" }
},
"props": { "content": [ { "value": "Bonus" } ] },
"animations": [
{ "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
"at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
]
},
// ── The red panel: a tilted row of two white texts, background derived around them.
{
"id": "amount-row",
"type": "flowBox",
"zIndex": 2,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "flash-rows", "w": "content", "h": "content", "rotation": -3 },
"9:16": { "flowIn": "flash-rows", "w": "content", "h": "content", "rotation": -3 },
"1:1": { "flowIn": "flash-rows", "w": "content", "h": "content", "rotation": -3 }
},
"props": { "direction": "row", "alignAcross": "bottom", "gap": 28 },
"animations": [
{ "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
"at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 4 } }
]
},
{
"id": "amount-prefix",
"type": "text",
"zIndex": 2,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "amount-row", "w": "content", "h": "content" },
"9:16": { "flowIn": "amount-row", "w": "content", "h": "content" },
"1:1": { "flowIn": "amount-row", "w": "content", "h": "content" }
},
"typography": {
"16:9": { "family": "GT Eesti Display", "weight": 400, "size": 85, "color": "#ffffff",
"case": "uppercase", "lineHeight": 0.9, "multiline": true, "horizontalAlign": "center" }
},
"props": { "content": [ { "value": "up\nto" } ] }
},
{
"id": "amount-hero",
"type": "text",
"zIndex": 2,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "amount-row", "w": "content", "h": "content" },
"9:16": { "flowIn": "amount-row", "w": "content", "h": "content" },
"1:1": { "flowIn": "amount-row", "w": "content", "h": "content" }
},
"typography": {
"16:9": { "family": "GT Eesti Display", "weight": 700, "size": 210, "color": "#ffffff" }
},
"props": {
"content": [
{ "price": { "amount": 300, "currency": "AUD", "locale": "en-AU" },
"typography": { "currencySymbol": { "size": { "ofRoot": 0.72 }, "baseline": 0.12 } } }
]
}
},
{
"id": "amount-bg",
"type": "background",
"zIndex": 1,
"layout": {
"mode": "derived",
"16:9": { "of": ["amount-row"], "ofBounds": "extents" },
"9:16": { "of": ["amount-row"], "ofBounds": "extents" },
"1:1": { "of": ["amount-row"], "ofBounds": "extents" }
},
"props": {
"fill": "#ed1c24", "radius": 11,
"padding": { "top": 24, "right": 50, "bottom": 12, "left": 33 }
},
"animations": [
{ "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
"at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 4 } }
]
},
{
"id": "cashback",
"type": "text",
"zIndex": 2,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "flash-rows", "w": "content", "h": "content" },
"9:16": { "flowIn": "flash-rows", "w": "content", "h": "content" },
"1:1": { "flowIn": "flash-rows", "w": "content", "h": "content" }
},
"typography": {
"16:9": { "family": "GT Eesti Display", "weight": 700, "size": 174, "color": "#ffffff" }
},
"props": { "content": [ { "value": "Cashback" } ] },
"animations": [
{ "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
"at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 8 } }
]
},
// ── Footnote, directly in the root so it sits outside the pulse.
{
"id": "footnote",
"type": "text",
"zIndex": 3,
"layout": {
"mode": "flow",
"16:9": { "flowIn": "flash-root", "w": "content", "h": "content" },
"9:16": { "flowIn": "flash-root", "w": "content", "h": "content" },
"1:1": { "flowIn": "flash-root", "w": "content", "h": "content" }
},
"typography": {
"16:9": { "family": "GT Eesti Display", "weight": 400, "size": 50, "color": "#ffffff", "tracking": 1 }
},
"props": {
"content": [ { "value": "with selected Laundry appliances*" } ],
"patterns": [ { "expression": "\\*$", "typography": { "size": { "ofRoot": 0.58 }, "baseline": 0.45 } } ]
},
"animations": [
{ "animation": "slideFade", "params": { "from": "top", "distance": 20, "durationInFrames": 12 },
"at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 16 } }
]
}
]
}
| Note | Binding target | Control |
|---|---|---|
| Editable text | bonus…value, amount-prefix…value, cashback…value, footnote…value |
text |
| Editable amount | amount-hero.props.content[0].price.amount |
number |
| Change colours | amount-bg.props.fill, *.typography[ratio].color |
colour |
| Pulse toggle per row | no numeric leaf — see stress points | — |
What the engine does with it¶
Resolve. The price run formats; { ofRoot } on the symbol multiplies against 210; five at
references resolve against start; three names expand into tracks.
Solve. Four content-sized texts measure; amount-row packs prefix and hero with a 28px gap,
bottoms aligned; amount-bg grows the row's extents by its asymmetric padding; flash-rows stacks
the three rows centred; flash-root adds the footnote 46px under them.
Animate. flash-rows pulses; bonus, the row, and cashback inherit; prefix and hero inherit the
row's scale and tilt in turn. amount-bg follows the row's animated extents. The footnote never
joins the pulse.
Place. Nine positioned elements; one derived fill; the browser paints glyphs.
What this shows¶
- Bare text and text-on-a-pill are the same layer type, differing only by the presence of
props.background. The scene's own background does the rest. - The only three-layer construct is, again, the two-text panel — the row, its members, and a derived fill.
Stress points¶
The same family-wide points as the full stack —
wipe versus transform-only, the derived background under a tilted row (-3° here), single gap
(the preset has 31 / 37 / 46, rounded to 34 and 46), first-line-only baseline on the UP / TO
stack, and the unbindable pulse toggle. No shadows in this variant, so background.shadow isn't
needed here.