Skip to content

Example: Red Hot Summer Sale

Manifest row 03 · built today as betta-red-hot-summer-sale

A five-panel column: RED and HOT on two red panels tilted 4° apart, SUMMER in red on a white panel, a huge SALE on a red panel, and a two-line blue footer SAVE BIG across / a range of products!* with SAVE BIG in yellow and the asterisk raised. Every panel carries a soft drop shadow, wipes on from the left in a 3-frame stagger, and all five breathe on a shared beat.

flowchart TB
    subgraph scene["scene-red-hot"]
        ROOT["rh-lockup<br/>flowBox column · absolute (120,120)"]
        TITLE["rh-title<br/>flowBox row · flow"]
        RED["rh-red<br/>text · flow · rot −4 · pulse"]
        HOT["rh-hot<br/>text · flow · rot 4 · pulse"]
        SUMMER["rh-summer<br/>text · flow · rot 2 · pulse"]
        SALE["rh-sale<br/>text · flow · pulse"]
        SAVE["rh-save-big<br/>text · flow · rot −2.1 · pulse"]
        RED -- flowIn --> TITLE
        HOT -- flowIn --> TITLE
        TITLE -- flowIn --> ROOT
        SUMMER -- flowIn --> ROOT
        SALE -- flowIn --> ROOT
        SAVE -- flowIn --> ROOT
    end

The document

{
  "id": "scene-red-hot",
  "durationInFrames": 75,
  "layers": [
    {
      "id": "rh-lockup",
      "type": "flowBox",
      "zIndex": 0,
      "layout": {
        "mode": "absolute",
        "16:9": { "x": 120, "y": 120, "w": "content", "h": "content", "anchor": "top-left" },
        "9:16": { /* own x/y */ }, "1:1": { /* own x/y */ }
      },
      // Source gaps: 21 / 9 / 31 between the four rows. One gap here — see Stress points.
      "props": { "direction": "column", "alignAcross": "centre", "gap": 20 }
    },
    {
      "id": "rh-title",
      "type": "flowBox",
      "zIndex": 1,
      "layout": {
        "mode": "flow",
        "16:9": { "flowIn": "rh-lockup", "w": "content", "h": "content" },
        "9:16": { /* … */ }, "1:1": { /* … */ }
      },
      "props": { "direction": "row", "alignAcross": "top", "gap": 43 }
    },
    {
      "id": "rh-red",
      "type": "text",
      "zIndex": 2,
      "layout": {
        "mode": "flow",
        "16:9": { "flowIn": "rh-title", "w": "content", "h": "content", "rotation": -4 },
        "9:16": { /* … */ }, "1:1": { /* … */ }
      },
      "typography": {
        "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 151, "color": "#ffffff",
                  "case": "uppercase", "horizontalAlign": "center" }
      },
      "props": {
        "content": [ { "value": "Red" } ],
        "background": { "fill": "#fe2121", "radius": 5,
                        "padding": { "top": 24, "right": 37, "bottom": 22, "left": 35 } }
      },
      "effects": [ { "type": "dropShadow", "color": "#000000", "opacity": 0.31, "blurPx": 8, "offsetX": 3.5, "offsetY": 4.3 } ],
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } },
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40, "delay": 30 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
      ]
    },
    {
      "id": "rh-hot",
      "type": "text",
      "zIndex": 2,
      "layout": {
        "mode": "flow",
        "16:9": { "flowIn": "rh-title", "w": "content", "h": "content", "rotation": 4 },
        "9:16": { /* … */ }, "1:1": { /* … */ }
      },
      "typography": { "16:9": { /* as rh-red */ } },
      "props": {
        "content": [ { "value": "Hot" } ],
        "background": { "fill": "#fe2121", "radius": 5,
                        "padding": { "top": 20, "right": 24, "bottom": 22, "left": 22 } }
      },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 3 } },
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40, "delay": 30 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
      ]
    },
    {
      "id": "rh-summer",
      "type": "text",
      "zIndex": 1,
      "layout": {
        "mode": "flow",
        "16:9": { "flowIn": "rh-lockup", "w": "content", "h": "content", "rotation": 2 },
        "9:16": { /* … */ }, "1:1": { /* … */ }
      },
      "typography": {
        "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 170, "color": "#fe2121",
                  "case": "uppercase", "horizontalAlign": "center" }
      },
      "props": {
        "content": [ { "value": "Summer" } ],
        "background": { "fill": "#ffffff", "radius": 5,
                        "padding": { "top": 23, "right": 61, "bottom": 24, "left": 56 } }
      },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 6 } },
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40, "delay": 30 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
      ]
    },
    {
      "id": "rh-sale",
      "type": "text",
      "zIndex": 1,
      "layout": {
        "mode": "flow",
        "16:9": { "flowIn": "rh-lockup", "w": "content", "h": "content" },
        "9:16": { /* … */ }, "1:1": { /* … */ }
      },
      "typography": {
        "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 290, "color": "#ffffff",
                  "case": "uppercase", "horizontalAlign": "center" }
      },
      "props": {
        "content": [ { "value": "Sale" } ],
        "background": { "fill": "#fe2121", "radius": 5,
                        "padding": { "top": 47, "right": 52, "bottom": 48, "left": 46 } }
      },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 9 } },
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40, "delay": 30 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
      ]
    },
    // ── Footer: two lines, mixed case, with "SAVE BIG" in yellow as its own run.
    {
      "id": "rh-save-big",
      "type": "text",
      "zIndex": 1,
      "layout": {
        "mode": "flow",
        "16:9": { "flowIn": "rh-lockup", "w": "content", "h": "content", "rotation": -2.1 },
        "9:16": { /* … */ }, "1:1": { /* … */ }
      },
      "typography": {
        "16:9": { "family": "GT Eesti Display", "weight": 700, "size": 58, "color": "#ffffff",
                  "lineHeight": 0.84, "multiline": true, "horizontalAlign": "center" }
      },
      "props": {
        "content": [
          { "value": "SAVE BIG", "typography": { "color": "#ffd007" } },
          { "value": " across\na range of products!*" }
        ],
        "patterns": [
          { "expression": "\\*$", "typography": { "size": { "ofRoot": 0.58 }, "baseline": 0.5 } }
        ],
        "background": { "fill": "#054f8e", "radius": 2,
                        "padding": { "top": 12, "right": 58, "bottom": 12, "left": 57 } }
      },
      "animations": [
        { "animation": "wipe", "params": { "direction": "from-left", "durationInFrames": 12 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 12 } },
        { "animation": "pulse", "params": { "scale": 1.05, "periodInFrames": 40, "delay": 30 },
          "at": { "mode": "cueOffset", "cue": "start", "offsetFrames": 0 } }
      ]
    }
  ]
}
Note Field path Control
Editable text rh-*.props.content[*].value text
Change colours rh-*.props.background.fill, rh-*.typography[ratio].color, rh-save-big.props.content[0].typography.color colour
Pulse toggle (headline / Save Big) rh-*.animations[1] on/off no bindable leaf — see Stress points
Tilt rh-*.layout[ratio].rotation degrees

Panel shadows are layer effects

Each panel above carries a drop shadow in the source artwork. A shadow is a per-layer effect, applied to the whole layer (glyphs and pill together), not a field on background — see Coordinate System & Sizing. Where text and its panel need different shadows, they become two layers: a text layer and a background layer spanning it, each with its own effects.

What the engine does with it

Resolve. Wipes at 0, 3, 6, 9, 12; five continuous scale tracks from pulse; the footer's asterisk resolves to 58 × 0.58.

Solve. Five texts measure unconstrained — the footer wraps at its authored \n since it is content-width — then rh-title packs RED and HOT with a 43px gap, then rh-lockup stacks four rows and centres them. Shadows are paint-only extents contributors; they never enter the packing.

Animate. Each panel pulses about its own content centre; the title row's two members pulse independently. The built component pulses all five about the lockup's centre.

Place. Seven elements, five of them rotated text pills with a drop-shadow filter.

What this shows

  • The simplest of the seasonal banners in structure — five text layers, one nested row, no spanning background — and the one that leans hardest on typography: mixed runs, a pattern, a tight lineHeight, uppercase via case.
  • Runs beat patterns for fixed styling. SAVE BIG is a run with its own colour; the pattern is reserved for the trailing asterisk, whose position depends on the copy.

Stress points

  1. background.shadow doesn't exist. backgroundSchema has fill, opacity, radius, padding only; every panel here carries a Photoshop drop shadow. The brief already counts "shadow spread" as an extents contributor (Coordinate System → Bounds) so the field is implied but never declared. Encoded as background.shadow: { color, opacity, blurPx, offsetX, offsetY } here.
  2. Uneven column gaps (21 / 9 / 31) with one gap. Same as 01 and 02.
  3. Pulse pivot and pulse toggle binding. Same as 01.
  4. Wipe is a clip animation. Same as 01.
  5. rotation on a flow member assumed. Same as 01.
  6. Pattern vs. run precedence. rh-save-big has both a run-level colour and a pattern that changes size/baseline on the asterisk. Text Foundation → Typography resolver orders base → run → part/role; where patterns sit in that chain (they exist in the shipped schema and are used everywhere here) isn't stated.