stageshow
Build faux product UI that looks like a real product, as a still illustration or an animated demo with a cursor. Browser and app windows, dashboards, phones, notifications, terminals, bento grids and cursor walkthroughs, framed, masked and cropped the way a marketing page, feature card or docs hero needs them. Use whenever someone wants a fake or mocked UI, a product demo or walkthrough animation, a hero or feature-card illustration of an app, a product shot in the style of a polished marketing site, or an animated cursor doing things in an interface, even if they never say "faux UI". React and plain CSS.
Install
npx skills add https://github.com/benjitaylor/skills --skill stageshowFraming
How the screen sits in its space decides whether it reads as a product shot or a screenshot in a box. Decide the framing before building the screen: a crop needs nothing past the crop, and it sets how big the type must be. SKILL.md has the summary; the numbers are here.
The rules
- One hero object, at most one or two satellites (a receipt, a phone, a detail card) that carry the point (
examples/static-card). - Keep what identifies the product: a title, the real chrome, the rows that give the action a reason. Logo and nav are not required; crop around the story.
- The medium picks the first layout: corner bleed for a hero beside copy, pinned bottom bleed for a card or tile, a phone pinned from the top. If it doesn't hold, try a second from the table below.
- Crop only filler. The payoff, the action, what the cursor touches and every real word stay whole; a list, diff or terminal may run one line off a bled bottom edge. A placeholder action the crop would slice is removed, not sliced; if the story does not fit, change the stage size or the layout. A button, chip or shadow sliced by the stage edge is the usual fault.
- Crop 10 to 40% of the hero off stage. The window or phone fills at least two thirds of the stage's width and runs off an edge. Labeled controls stay whole. Shapes that read when cut (a card, a slider track, a row of avatars) run off by a third or more.
- The stage masks everything (
overflow: hidden); one rounded corner per edge. Fades are for sliding tracks, stacks and charts, and for one crop: a window fading out at its foot. - Satellites straddle the hero's edge, about half outside, never fully inside or floating apart; fully outside, tie it back with a 1px leader ending in a dot.
- Distinct depth:
--ss-shadow-satelliteon a satellite sitting on the hero,--ss-shadow-groundfor a lone surface; a hairline on light edges; the shadow on the outlined shape, never a wrapper (examples/story). - Start flat and frontal; earn a tilt by comparing with the straight version at final size. Depth from shadow, z-order, scale and opacity; perspective only when asked.
- One gutter, measured in the final crop after any wrapper. The gutter is the hero's inset from the stage edge (the layout table gives it per layout); every other edge distance in the frame matches it. The exception is a satellite straddling in the wider column; a phone counts as a satellite.
- A full gutter or well past, on every edge. Crossing by under 24px or stopping a few pixels short is a mistake. Content near a container's bottom follows the same rule.
- A window keeps its device's shape: whole, 1.25:1 or wider; bled, what shows is at least square. Fix by crop or layout, never by squashing.
- What the story opens stays in frame, a gutter from every edge on every frame; place the trigger so the overlay has room.
- On a colored stage the hero is inset 8 to 20% from the sides and runs off the bottom, so the ground frames it on three sides.
- No dead corners at rest or anywhere in the loop. Fix crop or layout first; a satellite only with a role; a reserved column is filled from frame one.
- Anchored UI (popover, menu, toast) is not a satellite; it sits where the product would put it.
- Two satellites are two kinds, not a stack. A second satellite differs from the first in size and kind: a card and a small system object (a notification at its true size, a chip, a receipt), never two cards of one width on one edge, which read as a pile. A system banner sits where the system puts it, small, with the system's shadow, not a card's. Where the second object only points at something, use an annotation with a connector instead (
references/annotations.md), as Stripe does; film flagsframing: twin satellites. - A satellite covers chrome, not content: straddle where the window has a sidebar, header or margin (
examples/static-card). - Related surfaces get different jobs and weights; the result sits in the hero's quiet margin, never hiding settings, trigger or payoff.
- A small illustration may need more air than the app itself.
The layouts are recipes, not a requirement to crop every product; a whole window with even gutters is valid when its contents read and the brief asks.
The layouts
Recipes
Position the hero with plain CSS inside the Stage render. Reserve a wider column so the satellite straddles the hero edge evenly: gutter plus half its width (examples/static-card).
The shadow tokens in kit/stage.css (loaded by kit/stageshow.css), set by every theme:
--ss-shadow-hero: the one shadow for a page's hero windows (examples/gmail).--ss-shadow-window: whatWindowdraws by default; quieter, for a window that is not the hero.--ss-shadow-satellite: tighter on purpose, because it falls on a white window where a wide blur smears (examples/payout-flow).--ss-shadow-ground: a hairline and a long faint fall, for a small window or card on the ground (examples/looks).--ss-shadow-floatfor popovers,-cardfor small raised cards,-liftfor a carried thing.
Fades are for moving or stacked content: a 32px edge mask on a track, or the last 25 to 30% of a stack or chart. Use .ss-fade-b and .ss-fade-r; their length is --ss-fade (64px by default, so set --ss-fade: 32px for a track).
A window can fade out instead of running off, at its foot only: the top and sides stay whole and hard-edged, the fade covers the last third and only filler is under it. Fade with a mask, never a color overlay, so the window dissolves into whatever it sits on. Never fade a side, never fade the focal content, never fade more than one window in a scene (examples/fade-crop).
Small formats and the page
- The page is the ground. "White page" means a transparent stage: no plate (a backdrop or tinted field the window floats on) and no panel behind the device on a white or dark page. A hero's window bleeds or fills; whole and centered with air on four sides, it floats.
- Unframed is the developer's call. Asked for no framing, set
unframedon the Stage and drop the ground: the window, card or phone sits whole, centered, with an even margin at least as deep as its shadow, so the shadow is never cut. Nothing bleeds, so nothing needs a crop rule; everything else in this file still holds, and so do restraint, story and motion. Never choose it on your own: a floating window is the fault this file exists to fix. compactis a second design, not a squeeze. Every demo that can sit in a phone column ships one at the column's width: 358 in a 390 viewport, switched on below the host width where the wide design stops fitting (compact={{ below: 560, width: 358, height: 320 }}for a wide scene; the height follows the design). Switch composition onlayout: crop closer, drop or stack a satellite, keep action and payoff whole. A card that already fits at 358 needs none.- Small formats crop harder: a 400px card holds a third of a dashboard at full size, not all of it at a third.
- The stage is the shot. The tile is cut to the stage's shape and the stage fills it edge to edge; never a stage boxed in another ground.
- Size the design for the slot. A lone card in a 680px column wants 500 to 560 wide, so its text reads at 15 to 20px.
- A lone phone in a column of figures keeps the column's width, centered at design size on the stage's own ground.
- Moving a window keeps the bleed. After any move or resize, recheck what the crop cuts and what overlaps, remove sliced placeholder actions, re-shoot the whole loop.
Phones
- A satellite phone starts at 40% of the window's visible width and at least 220px wide; let it run off the bottom rather than shrink.
- Notifications stay at true iOS proportion (Live Activities excepted); a phone so small its notification needs enlarging is too small (
examples/phones). - A phone cropped at the top loses its status bar and nav bar together. The crop falls inside the content below the nav, never between the island and the nav.
- In a 400px host a phone is cropped: pinned a gutter from one edge, off the other, screen 1:1, stage a little taller than wide; never whole (
examples/phone-touch). - A banner is native width and never cut. A notification spans the screen minus 18pt each side, at the phone's own scale; nothing narrows it. A phone cropped at a side is composed so the banner shows whole (crop where no banner sits, or show the phone's full width) and film flags a cut banner as
cut by the crop.
Checks
film.mjs prints framing: and cut by the crop: on every shot; clear them on the still before motion work (rehearse.md, What to watch), or say why a line is deliberate. Boxes in flight are judged where they settle. Under a Camera the wide shot is judged as usual, moving frames skipped, and a settled zoom judged against the camera's own crop with its focus clear of the feather.
Is the hero's top-left visible? Is a bleed a deliberate crop, not a sliver? Does each satellite connect to its hero with clear depth? Is a faded window faded only at its foot, over filler, with a mask? Is the phone readable at the host's real width, and does a wide scene have its compact design (Small formats and the page, above)?