@@ -255,6 +255,11 @@ whose permissions are too broad.
255255
256256## Local compose
257257
258+ When composing signage pages—including posters, ads, menus, schedules, and
259+ video-backed pages—read [ Composition and visual direction] ( references/composition.md )
260+ before choosing a layout. It covers reference research, useful density, independent artwork,
261+ readability, and playlist-wide visual review.
262+
258263Write JSON, render a PNG, look at that PNG, iterate. Compose is not billed.
259264Uploads and playlist writes are billed. Iterate ` compose render ` and read
260265` <output>.layout.json ` before any ` media upload ` .
@@ -309,10 +314,13 @@ this computer only when the user asked to view the still here. Agent vision
309314uses the file path, not ` --open ` . Raster a diagram to PNG or WebP at canvas
310315size, upload it, and place it as ` image ` . HTML is not a primitive.
311316
312- Choose raster dimensions for the screen's ** physical content viewport** , not
313- just the playlist's logical canvas or the full panel when content is
314- letterboxed. A 1920×1080 slide displayed in a 3840×2160 viewport enlarges
315- every flattened element 2×, even when its original logo is high resolution.
317+ Plan the canvas from the user's intended output, orientation, and content
318+ viewport. Honor a requested 1920×1080 deliverable even when reviewing on a
319+ larger monitor; desktop dimensions alone do not change the brief. For final
320+ physical display quality, choose raster dimensions for the ** physical content
321+ viewport** , excluding letterboxing. A 1920×1080 slide displayed in a
322+ 3840×2160 viewport enlarges every flattened element 2×, even when its original
323+ logo is high resolution.
316324Preserve aspect ratio: use ` objectFit: "contain" ` for complete marks and
317325` cover ` for intentional cropping; ` fill ` can distort mismatched proportions.
318326Source images must support their actual painted pixel dimensions, including
@@ -353,16 +361,17 @@ and tables. Flattening all copy into an SVG or PNG hides it from text fitting,
353361font checks and safe-area diagnostics. Keep code-native illustrations as image
354362assets when useful, while leaving adjacent explanatory text measurable.
355363
356- Start with a recipe from ` compose catalog ` : ` title ` , ` split-image ` , ` cards ` ,
357- ` table ` , or ` overlay ` . Recipes expand to the same ordinary compose nodes;
358- they keep a 5% content inset and the normal readable type floors. Set ` width `
359- and ` height ` to the physical content viewport. Omitted dimensions are
364+ Recipes from ` compose catalog ` (` title ` , ` split-image ` , ` cards ` , ` table ` ,
365+ ` overlay ` ) are optional starting points, not a visual system for every page.
366+ Use ordinary compose nodes when the content calls for a different hierarchy.
367+ Recipes keep a 5% content inset and the normal readable type floors. Set
368+ ` width ` and ` height ` to the intended content viewport. Omitted dimensions are
3603691920×1080. ` split-image ` uses ` contain ` by default; ` cover ` crops proportionally.
361370The overlay recipe defaults to a transparent canvas and an approximately 89%
362371opaque plate with independently opaque text. Omit its ` image ` to layer the
363372PNG over native video; include a local image for a flattened still.
364373
365- For several pages, author one batch instead of separate render/ review scripts :
374+ For several pages, ` compose batch` provides a shared render and review path :
366375
367376``` json
368377{
@@ -424,9 +433,10 @@ require server checks. Raster QA alone never proves the playlist is valid.
424433` flex: 1 ` , or the copy lives in a ` Box ` with ` pin: "bottom" ` . A ` Column `
425434without ` flex: 1 ` shrinks to its text and sits at the top of the Frame.
426435
427- For text over images or video, prefer a subtly translucent backplate so the
428- imagery shows through. Start around 85–92% opacity as a visual preference,
429- then adjust for readability. Compose colors use ` #RRGGBBAA ` : for example,
436+ For text over images or video, fit support snugly around the copy: a
437+ translucent plate or raster gradient can retain the picture while making type
438+ readable. Choose opacity from the actual imagery; a large nearly opaque panel
439+ is not the default. Compose colors use ` #RRGGBBAA ` : for example,
430440` Box.background: "#000000E0" ` is black at about 88% opacity. Add padding
431441around the copy, such as ` "padding": "l" ` . Apply alpha to the backplate
432442background only; keep text opaque, such as ` "color": "#FFFFFF" ` , rather
@@ -456,20 +466,17 @@ Overlay still (transparent Frame, lower-third plate):
456466}
457467```
458468
459- Do not copy a short strip Frame from an older deck. Author the overlay at
460- the full slide resolution (1920×1080 in this example) with ` pin: "bottom" ` .
461- Leave a right pocket for the playlist
462- wordmark: shrink-wrap ` Column ` plus ` Spacer ` . The 5% wordmark rect
463- ` { x: 1424, y: 946, width: 400, height: 80 } ` sits on bottom-plate body
464- copy. A tighter corner that clears copy is
465- ` { x: 1544, y: 996, width: 352, height: 68 } ` .
466-
467- Side rail: shrink-wrap ` Box ` plus ` Spacer ` , not ` pin: "left" ` or ` "right" ` .
468- A ` Row ` with ` { Box, Spacer flex: 1 } ` is a left rail. Reverse the children
469- for a right rail. Do not put ` flex ` on the copy ` Box ` or Yoga grows it
470- across the canvas. Force newlines in title and body so min-width lands
471- around 740–900 px; one long line almost fills the frame. Mix left, right,
472- and bottom across a photo sequence. Bottom stays right for a lower-third.
469+ Author the overlay at the full slide resolution (1920×1080 in this example),
470+ so the type ramp stays at the intended scale. The pinned bottom plate above
471+ is a full-width lower-third example, not a requirement for every image. Keep
472+ any separate wordmark clear of the copy and preserve its proportions.
473+
474+ For snug side support, use a shrink-wrapped ` Box ` plus ` Spacer ` rather than
475+ ` pin: "left" ` or ` "right" ` , which stretches the cross axis. A ` Row ` with
476+ ` { Box, Spacer flex: 1 } ` makes a left rail; reverse the children for a right
477+ rail. Omit ` flex ` on the copy ` Box ` to avoid growing it across the canvas.
478+ Choose line breaks and measured container sizes for the actual copy and
479+ picture; inspect the resulting plate bounds.
473480
474481``` json
475482{
@@ -496,9 +503,10 @@ and bottom across a photo sequence. Bottom stays right for a lower-third.
496503}
497504```
498505
499- Playlist: photo ` layer ` 0 + overlay ` layer ` 1, both ` content_fit: "fill" ` on a
500- 1920×1080 canvas. Eight-digit hex is how the Frame stays transparent and the
501- plate keeps alpha.
506+ Playlist: photo ` layer ` 0 + overlay ` layer ` 1 on a 1920×1080 canvas. Use
507+ ` content_fit: "fill" ` for a matching-aspect full-canvas overlay; preserve the
508+ photo proportions with ` contain ` or intentional ` cover ` cropping. Eight-digit
509+ hex is how the Frame stays transparent and the plate keeps alpha.
502510
503511Wordmark: playlist ` image ` primitive with a ` rect ` . Soft-open: omit that
504512primitive until a named page. Do not pin a logo in compose; ` pin ` stretches
@@ -636,9 +644,11 @@ These are playlist document fields the CLI sends. The control plane accepts
636644swipe types and object ` enter ` .
637645
638646Default pages: ` transition ` is ` { "type": "crossfade", "duration_ms": 200 } ` .
639- Author crossfade unless swipe or ` enter ` is the intended emphasis. One overlay
640- ` enter ` is enough; do not put ` enter ` on every primitive, including the
641- wordmark.
647+ Author crossfade unless swipe or ` enter ` is the intended emphasis. Select
648+ foreground artwork or a short headline for restrained entrances; leave menu
649+ items, prices, and other information stationary. Video already supplies
650+ motion. Choose animated layers by purpose, without a fixed layer quota or
651+ an entrance on every primitive.
642652
643653` transition.type ` is ` crossfade ` , ` swipe-left ` , ` swipe-right ` , ` swipe-up ` , or
644654` swipe-down ` . ` duration_ms ` is required and runs from 0 through 60000. When
@@ -659,6 +669,10 @@ fields and not CLI flags. Do not send duration or delay inside `enter`.
659669To slide text in over a still or video, compose the text and its translucent
660670plate into a transparent PNG, place that image above the background's layer,
661671and apply ` enter ` to the overlay image. Keep the background independent.
672+ The same mechanism works for independent foreground artwork with alpha.
673+ Leave transparent breathing room around moving ink within its raster and
674+ primitive rect so entry motion does not clip its edges; do not stretch the
675+ asset to compensate. Keep supported timing constants unchanged.
662676Preview the first activation and a loop replay on each intended player; check
663677that the overlay begins hidden, enters within its rect, and retains the
664678expected layer order. A settled screenshot alone cannot verify animation.
0 commit comments