Skip to content

Example: Betta Buys Cashback

Manifest row 12 · built today as betta-buys-cashback

Three flush, equal-width blocks stacked: the yellow Betta wordmark, a yellow BUYS panel with blue text, and a red panel carrying three rows of white copy — PLUS beside a small UP/TO beside $300, then Cashback, then on selected models*. Every block is exactly as wide as the widest, so the stack stays a clean column however the copy changes. The blocks wipe on in turn; the two panels pulse.

flowchart TB
    subgraph scene["scene (16:9 graph)"]
        ROOT["bb-stack<br/>flowBox · column · alignAcross stretch · absolute"]
        LOGO["bb-logo<br/>image · flow · alignAcrossSelf centre"]
        BUYS["bb-buys<br/>text 'BUYS' on yellow · flow (stretched) · pulse"]
        RED["bb-red<br/>flowBox · column · flow (stretched) · pulse"]
        REDBG["bb-red-bg<br/>background · derived of bb-red"]
        R1["bb-red-line1<br/>flowBox · row · flow"]
        PLUS["bb-plus<br/>text · flow"]
        UPTO["bb-up-to<br/>text · flow"]
        AMT["bb-amount<br/>text price $300 · flow"]
        HERO["bb-hero<br/>text 'Cashback' · flow"]
        FOOT["bb-footnote<br/>text · flow"]
        LOGO -- flowIn --> ROOT
        BUYS -- flowIn --> ROOT
        RED -- flowIn --> ROOT
        REDBG -- of --> RED
        R1 -- flowIn --> RED
        PLUS -- flowIn --> R1
        UPTO -- flowIn --> R1
        AMT -- flowIn --> R1
        HERO -- flowIn --> RED
        FOOT -- flowIn --> RED
    end

The document

{
  "id": "scene-betta-buys",
  "durationInFrames": 75,
  "layers": [

    // ── The red panel: a derived background under the inner column. Because the column is a
    //    stretched flow member, its extents are the stack's full width, so the panel is too.
    {
      "id": "bb-red-bg",
      "type": "background",
      "zIndex": 0,
      "layout": {
        "mode": "derived",
        "16:9": { "of": ["bb-red"], "ofBounds": "extents" }
        // "9:16": …, "1:1": …
      },
      "props": { "fill": "#ed1b24", "radius": 0, "padding": { "top": 21, "right": 12, "bottom": 21, "left": 12 } },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 8 } }
      ]
    },

    // ── Root column. `stretch` gives every member the widest member's width — the "equal
    //    rectangles" the source draws — while the column itself stays content-sized.
    {
      "id": "bb-stack",
      "type": "flowBox",
      "zIndex": 1,
      "layout": { "mode": "absolute", "16:9": { "x": 120, "y": 120, "w": "content", "h": "content", "anchor": "top-left" } },
      "props": { "direction": "column", "alignAcross": "stretch", "gap": 29 }
    },

    // ── Wordmark: fixed width, aspect height. Opts out of stretch so it isn't distorted.
    {
      "id": "bb-logo",
      "type": "image",
      "zIndex": 2,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-stack", "w": 660, "h": "content", "alignAcrossSelf": "centre" } },
      "props": { "src": { "kind": "bundled", "path": "effects/betta-buys-cashback/betta-wordmark.png" } },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
      ]
    },

    // ── BUYS: a stretched text layer whose paint-only background fills the stretched box.
    {
      "id": "bb-buys",
      "type": "text",
      "zIndex": 2,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-stack", "w": "content", "h": "content" } },
      "typography": { "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 248, "color": "#054f8e", "case": "uppercase", "horizontalAlign": "center" } },
      "props": {
        "content": [ { "value": "Buys" } ],
        "background": { "fill": "#ffd007", "radius": 0, "padding": { "top": 53, "right": 24, "bottom": 52, "left": 23 } }
      },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 4 } },
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 30 } }
      ]
    },

    // ── Inner red column: three rows of copy, centred, stretched to the stack's width.
    {
      "id": "bb-red",
      "type": "flowBox",
      "zIndex": 2,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-stack", "w": "content", "h": "content" } },
      "props": { "direction": "column", "alignAcross": "centre", "gap": 20 },
      "animations": [
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 30 } }
      ]
    },
    {
      "id": "bb-red-line1",
      "type": "flowBox",
      "zIndex": 3,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-red", "w": "content", "h": "content" } },
      "props": { "direction": "row", "alignAcross": "centre", "gap": 24 }
    },
    {
      "id": "bb-plus",
      "type": "text",
      "zIndex": 4,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-red-line1", "w": "content", "h": "content" } },
      "typography": { "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 116, "color": "#ffffff", "case": "uppercase" } },
      "props": { "content": [ { "value": "Plus" } ] }
    },
    {
      "id": "bb-up-to",
      "type": "text",
      "zIndex": 4,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-red-line1", "w": "content", "h": "content" } },
      "typography": { "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 41, "color": "#ffffff", "case": "uppercase", "lineHeight": 0.83 } },
      "props": { "content": [ { "value": "Up\nTo" } ] }
    },
    {
      "id": "bb-amount",
      "type": "text",
      "zIndex": 4,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-red-line1", "w": "content", "h": "content" } },
      "typography": { "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 116, "color": "#ffffff" } },
      "props": { "content": [ { "price": { "amount": 300, "currency": "AUD", "locale": "en-AU" } } ] }
    },
    {
      "id": "bb-hero",
      "type": "text",
      "zIndex": 3,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-red", "w": "content", "h": "content" } },
      "typography": { "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 116, "color": "#ffffff" } },
      "props": { "content": [ { "value": "Cashback" } ] }
    },
    {
      "id": "bb-footnote",
      "type": "text",
      "zIndex": 3,
      "layout": { "mode": "flow", "16:9": { "flowIn": "bb-red", "w": "content", "h": "content" } },
      "typography": { "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 40, "color": "#ffffff" } },
      "props": {
        "content": [ { "value": "on selected models*" } ],
        "patterns": [ { "expression": "\\*$", "typography": { "size": { "ofRoot": 0.58 }, "baseline": 0.45 } } ]
      }
    }
  ]
}
What the notes want editable Bound field Control
Every run's text bb-*.props.content[0].value text
Amount bb-amount.props.content[0].price.amount number
Panel colours bb-buys.props.background.fill, bb-red-bg.props.fill colour
"UP TO" on/off see Stress points
Pulse toggle per panel bb-{buys,red}.animations[pulse].params.scale see Stress points

What the engine does with it

Resolve. $300 formats to parts; the footnote's asterisk shrinks and lifts via its pattern; four wipes stagger 0/4/8; two pulses start at frame 30.

Solve. Leaves first, then bb-red-line1, bb-red, bb-red-bg, and bb-stack last. The interesting step is stretch: bb-stack is "content" wide, so it first takes the widest member's intrinsic width (the 660px wordmark), then hands that width back to bb-buys and bb-red as their resolved w. bb-buys's yellow fill paints across the full stretched box with the word centred inside it; bb-red-bg takes the stretched column's extents, so the red is flush with the yellow. bb-logo opted out with alignAcrossSelf: "centre" and keeps its 660.

Animate. bb-buys pulses about its own centre. bb-red's pulse reaches all five text layers through flowIn and the red panel through of; the panel follows the scaled union.

Place. Eleven elements, three of them non-visual containers that emit nothing.

What this shows

  • stretch in a content-sized container is the equal-width column. No grid, no measured max-content pass in component code — the sizing-collapse rule gives it for free.
  • Two ways to make a panel, again. BUYS is text with a background; the red block is a background under a column. Same look, chosen by how many layers the panel holds.
  • Deep but legal. bb-plus is four flow links from the scene (line1 → red → stack), under the cap of five; a fourth row of copy inside line1 would not be.

Stress points

  • stretch inside a "content"-sized container is a two-pass resolve the brief doesn't state. The sizing-collapse cases cover content-in-content and fraction-in-content; a stretched member in a content-sized container is a third case (intrinsic pass, then distribute) and it's the one this whole component rests on. It works the CSS way, but it should be written down.
  • Removing a run needs a layout-affecting "collapsed" state, and it has to be bindable. The built upTo.enabled: false removes the stack and re-centres the row. The brief has no per-layer "not rendered, don't reserve space" flag, opacity: 0 keeps the slot, and bindings can't add or remove layers (Positioning → Bindings). This is a real, common authoring switch with no home.
  • Depth cap is close. bb-plus sits at four links; a common lockup shape is one nesting away from the five-link ceiling (Positioning → Depth cap). Not a hole, but the cap's rationale ("should be a component") was written before flow-inside-flow-inside-flow was the normal way to draw a three-row panel.
  • wipe is not transform/opacity, no per-member spacing (24 vs 20 inside the red panel, and the built upToOffsetY nudge), pulse pivot, per-part pulse toggle — as on the Home Appliance page.