Deft
Ask AI

Search

Search skills, authors and topics

benjitaylor / skills

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 stageshow

Craft

The rules for the still frame. Framing is in framing.md; everything that moves is in motion.md.

Index: Restraint, Type, Spacing and alignment, Icons and logos, Color and looks, Density, Chrome and devices, A page of demos, Recipes.

Restraint

  • Count the real strings: the title, the thing acted on, what is typed, the result; usually 3 to 7. The rest is Bar.
  • A word budget is not an element budget. A calendar needs a populated week, a search a collection, a filter enough rows.
  • Lists are made of Row; hand-built rows are where lists go wrong (examples/gmail).
  • Bars are the product's filler; the subject is words. Bars where the eye lands read unfinished (examples/spotify).
  • In a still the subject is real, words and values; a sidebar or list of bars beside it is filler and fine.
  • Where the story touches a list, two or three real rows and at most as many bars. Film flags skeleton ladder at realistic fidelity only.
  • In a small surface a row is one line: in a popover, a card or a tile, avatar, name, value. No gray bar under a real name there; filler beside real words reads as noise.
  • Named avatars take their own color (the kit default); gray discs are filler only.
  • Label and value match: both words or both bars. A blank pill beside a real control is dead; leave it out.
  • Keep the domain's signature content: album art, thumbnails, avatars; swapped for the feature's signal it is a generic list (examples/dark-bento).
  • When the output is the subject (a log, a diff, a terminal), real lines are the content: short and few, the rest of the screen quiet.
  • A terminal is a character grid: a spinner and a text tick share one 1ch cell; each line owns its mark, swapped in place (a tick on its own line in examples/ruled-bento).
  • One number gets to be big; the rest are lines (examples/subscriptions).
  • Placeholder widths vary row to row (60, 45, 70, 52).
  • A date-range band runs week by week with air between rows, fades where the range continues, and is capped only on the real start and end days, each a solid disc (a circle, not a rounded square) in the accent with the day in white.
  • Say it once. One clear link for the feature, not its signal in header, satellite and every row; a second confirmation only when it differs (examples/dark-bento).
  • No masthead for a fictional name: no monogram, logo, sidebar and nav in every crop; a real mark only when recognition needs it.
  • The product's name appears only when the brief leans on it and the real product would show it there: a notification's app name, the app's own title bar. A brief that just names the product doesn't need it on screen; the page around the demo already says it.
  • Simplify before polishing: the fewest surfaces and strings that explain the product, then borders and badges only where earned.
  • Simplification keeps the character. Cut repeated framing before meaningful imagery; an empty white card is not the cure.
  • Judge the silhouette before the details. At thumbnail size the subject, support and empty space make one picture.
  • Compose objects, not an inventory of cards: hierarchy from proportion, space, material and ground, not more boxes (examples/story).
  • The heaviest marks sit on what the story is about; never a 2px black underline under a gray bar.
  • Space follows the story. The payoff gets the room; no empty band mid-window while the result is pushed into the crop. No gap above a card, no cream under a phone's last row, no void between a stat's label and its number.
  • Dots, halos, badges and focus rings sit fully inside the content edge.
  • Check imagery at its smallest size and over every surface it crosses; a pale thumbnail can look see-through.
  • A specimen still describes one plausible app: title, toolbar, navigation and body agree.
  • Nothing outsized. The hero element sits in proportion to a real screen.
  • Realism is the developer's call. At fidelity="realistic" or when asked, plausible content everywhere, still with room to breathe (examples/wikipedia).
  • Character is real too. A styled brief gets its colors, radii, mascot and motion in full, still a shippable product under these rules.

Type

  • Fit the text before fixing the box. A control's border-box holds content, padding and border; flex centering does not make it fit.
  • Small text gets a line height of at least 1.3 and insets on every side; check the text slot's own clearance.
  • Real sizes, then scale: 11 to 15px UI text, scaled by the stage; never 7px type.
  • One family: the product's or the system stack. Monospace only for code, identifiers and aligned numbers, never a technical product's labels (examples/ruled-bento).
  • Keep only the text needed to recognize the product and follow the beat; extra logs and timestamps are noise.
  • Sentence case unless the real product has caps; hierarchy from spacing and one weight step.
  • No real headings. A faux app's title is a div, never h1 to h6: the demo lives in someone's page outline.

Spacing and alignment

  • A button keeps 8 to 12px from the field, line or card edge above it; sitting on the field it reads as touching (film flags tight control), and much more lets it drift off from the field it belongs to. A floating card (a comment popup, a toast) pads the same all round, measured to the avatar's and button's edges, not their words: 20px above the name with 14px at the side reads as a miss. A composer's Post or Send sits close under its box; padding sized for the posted card goes on the card, not between box and button.
  • Words keep a 12px inset from the edge of a window, card, panel or hovered row; film flags tight inset under 8px. Bars may bleed, words do not.
  • Row padding is 12px, 8px for a dense list; a one-line row three times taller than its words is a fat row.
  • A title over its subtitle reads as one item: 4 to 6px of ink between the lines, a 1.3 line height plus a 2px gap. The air comes from the row's padding (examples/gmail).
  • Bars sit in the same line box as the text they replace, so a skeleton row keeps the real row's pitch.
  • Bars keep their lines' white space, not line pitch: 6 and 5px bars want 7px between; a trailing time sits on line one.
  • Text never touches a neighboring box: a fixed gap of 8 to 12px, never leftover space-between (touching).
  • A placeholder bar follows the text rules: it never touches or runs under an avatar, badge, icon or bar.
  • Center a label on its letters, not its line box. Capitals sit low in a plain line box, so a label beside an icon (a check and "Sent" in a button), in a pill, or an initial in a disc reads low. Trim the label: text-box: trim-both cap alphabetic (the kit's Badge and Avatar already do).
  • Nested shapes pad evenly (a pill inside a field, an icon inside a button: the same distance from every edge), and the outer radius equals the inner radius plus that padding.
  • Carry the inset round the bottom; a comfortable top does not excuse a cramped action.
  • Content clears the bar above it: a page's first heading sits at least the content's side inset below a toolbar or title bar's rule, never just under it.
  • Whitespace groups: a title sits closer to its metadata than to the next record; never spread lines equally to fill a frame.
  • Align the reading edge, then the visible ink. A small icon aligns with the title's first line; size each lane before nudging.
  • Center on the visible face. Take tracking back with a negative margin-right; a pair centers as a pair.
  • Field text centers cap height to baseline (text-box: trim-both cap alphabetic), as the kit's Field does (examples/controls).
  • A popup under a field draws its ring inset so its sides line up with the field's.
  • A control hugs its content; never flex: 1 with its contents at one end (lopsided).
  • Fix overflow by fitting the content, not by widening the container; re-measure padding after (uneven).
  • Columns line up: every row shares one grid template with fixed tracks; optional content keeps its track empty; bars vary inside their track.
  • A dot, badge or ring is not a column: in the gutter or on the avatar, 6px clear, inside the row's inset (tight mark, examples/gmail).
  • No near-tangents. Edges 1 to 3px apart read as an accident: line them up exactly, or part them by at least the gutter and never under 8px.
  • Don't end just short of an edge: real room, or move it down and crop it (framing.md, rule 11).
  • A centered amount centers its digits; $45 centered as one string sits right of center to the eye. The currency sign hangs off the left, outside the centering: the digits are the centered box and the sign is absolutely placed at right: 100% inside it. A rolling amount rolls only the digits.
  • Links have no hover surface; a control gets a surface visible at rest.
  • Hover and selection fills are inset and rounded; negative margins go on the list, not the row; copy a real product's square fills (examples/wikipedia).
  • Clone a real product's menu from its computed styles (examples/wikipedia).
  • A trigger with its popover open keeps its pressed look: data-open, styled like [data-hover] (examples/hero-demo).
  • A progress ring is one track and one arc, round caps, never butt-capped dashes chopped into segments; a week of days goes in the dots beside it. A number in a ring centers on its digits: Roll with proportional figures, not Count, whose fixed-width digits leave a 1 off center; trim it to cap height (text-box: trim-both cap alphabetic) and set an icon above it apart, so the figure the eye lands on is the ring's center, not the pair's box.

Icons and logos

  • Imagery earns its place: real imagery or a good icon, never invented illustration; gray art only for empty states.
  • Icons come from a set, never from your hand. The product's own pack first (its dependencies, icons folder, sprite or Icon component); otherwise a set chosen on purpose (below), used at one size and one stroke width throughout. A glyph drawn by hand is the first tell of generated UI: its weight never matches its neighbors (film flags mixed icons).
  • No hand-drawn marks (pin, cursor, badge, ringed dot, chip, contactless arcs): from the kit or the product's assets, else leave them out.
  • A drawn mark gets a close-up beside the real thing, seen as a stranger would.
  • Otherwise choose a set on purpose: kit Icon filled for Apple, Material Symbols for Android, else Phosphor, Iconoir, Radix, Tabler or Heroicons; Lucide deliberately, never reflexively.
  • Apple chrome uses SF-style glyphs; a Lucide glyph only where no system glyph exists, as examples/finder does.
  • One family, one stroke weight, one size (14 to 16px) per scene; equal boxes can still hide a heavy fill beside a thin outline.
  • Even glyphs in even boxes; an odd icon in an even box snaps when motion ends (half-pixel glyph).
  • Fixed boxes don't shrink: an icon button or chip in a flex row gets flex: none (squeezed).
  • Small icons are filled. At 14px and under, and in tinted tiles, use the filled style; thin outlines go wiry (examples/kit-extras).
  • Icons are one opaque color (--ss-icon, --ss-icon-faint, opaque text tokens); fade with opacity on the svg; no duotone.
  • Emoji are content, never icons. A playful look rounds the icons, it does not swap them for emoji (emoji icon).
  • Other products' logos come from BrandMark or a project asset, in original proportions and colors; never a look-alike (brands.md).
  • Center glyphs by eye: shift a play triangle toward its point by a sixth of its width.
  • AI features get no sparkle, wand or star, and no purple as the AI color. Show what the AI does. The product's own icon is fine even when it is a sparkle (examples/hero-demo uses its product's list-sparkle); a sparkle as generic AI decoration is not.
  • No invented logo. A letter or glyph in a colored square is not the product's mark (film flags invented mark). Use the product's real mark, or a plain text wordmark.

Color and looks

  • One neutral family for window, sidebar and cards; a colored ground or another material differs on purpose.
  • Five inks and one accent to start; status colors mean status; an expressive product may need more, with a clear hierarchy.
  • No redundant tonal borders. Try the fill alone; add the border for separation, state, or because the product draws it.
  • Quiet is not faint: check text, actions and surface against the real ground.
  • A gradient or glow serves the story or goes.
  • The hero isn't outlined to point at it. An accent ring with a soft halo around the card the story is about reads as a focus state nobody gave it, and a dot beside its label is a mark with no meaning. Lift it the way the product would: its own surface, the raised shadow, a label in the accent. A ring is for a field that really has focus.
  • A filled button keeps its color under the pointer: one step of its own color, never a gray swap; success is the one real change.
  • In mono, mark the thing acted on with a solid step in surface and weight, never a translucent wash (examples/dark-bento).
  • Nothing translucent overlaps. Overlapping avatars are Facepile; stacked chips or cards take opaque fills (onion skin, examples/figma).
  • On dark themes depth comes from a lighter surface, not shadow; satellites go on --ss-raised (examples/linear).
  • A light document over a dark workspace stays a readable light document with its own foreground and edge.
  • The light inks are tuned for white cards on gray canvas; on white, step them up a tone.
  • Status color stays legible: success, pending and disabled distinct, never passing through mud.
  • Contrast has a purpose. A dark editor can be identity with a clear edge; a near-black strip for no reason is a hole.
  • Grounds support the subject; a muddy halo standing in for foreground contrast does not (backgrounds.md).
  • Build once, check every look: theme (light, dark, blueprint, glass), palette (accent, mono), fidelity (minimal, standard, realistic); Text for words, tokens for color.
  • A light pass does not prove dark. Film every look the request names; --dark films the dark theme; theme="dark" is always dark.

Density

  • Density follows the story. Would a person do this here? No element quota; empty space can be hierarchy; enlarge content before adding anatomy.
  • Never under about 320 wide for a demo; a bento tile or a satellite card may be smaller. Below that, keep the action and readable type, drop the desktop anatomy.

Chrome and devices

  • A messaging app on an iPhone keeps Messages' conventions unless the brief gives it a look: blue sent bubbles (--ss-accent as the kit sets it), gray received, the plus and the mic in the composer, reactions on the bubble. "Our messaging app" names the product, not a palette; inventing an accent and a composer makes it read as a fake (examples/messages).
  • Does the frame help? Browser chrome only when the browser is part of the picture; a desktop app gets its real chrome (examples/chrome).
  • Chrome for the task: a sharing panel needs a document, permissions and an action; a browser bar explains nothing.
  • Columns run the full height of their window (Window sidebar); a column that stops short leaves a seam (examples/finder).
  • Window chrome is fixed at 38px; barSize="tall" (64px) only for a real icon-and-label toolbar (examples/finder-glass).
  • Never draw platform chrome from memory; open the real app or a screenshot first.
  • macOS 26 Finder: solid white window, only the floating sidebar is glass, gray glyphs, selected row on a gray pill (examples/finder-glass).
  • Quiet browser windows center their content and carry a rounded, centered URL pill (examples/static-card).
  • A document that is not the hero is content: a ruled page with real margins, not another window.
  • Phones come from the kit (the frames are in kit.md, Phone); photo falls back to pro, never an empty bezel (examples/phones).
  • System surfaces keep their real proportions. Never shrink the iOS clock; the payoff wins by content, timing and crop (examples/dynamic-island).
  • A notification is one bold title over its message with Notification's geometry and a real app icon (examples/phones).
  • Nothing sits under the island; a sheet or grabber starts below the status bar.
  • The status bar and home indicator come from the Phone, true to iOS, never drawn by hand. A second indicator inside the app shows as a bar that scales into place and a sliver under the real one. An app that covers a dark screen turns the Phone's dark off as its surface arrives, and both fade to dark.
  • A phone is lit from its first frame: never dimmed, blacked out or faded up.
  • A phone background has a job: LockScreen for notification and island stories; never invented home-screen icons (examples/dynamic-island).
  • A phone may straddle a window but never covers the amount it explains.
  • Several windows: choose the active step; quieter contrast and shadow push the others back (examples/hero-demo).

A page of demos

  • One figure language: one ground, one gutter, one window shadow (--ss-shadow-hero), one radius set; a different shadow says something or is a mistake.
  • Fewer, stronger demos. No little labeled containers or text tiles when the demo says it.
  • A comparison pair shows a different demo from the one just above it.
  • A bad example is marked in the page's own annotation style, never with crosses or scrawls.

Recipes

  • Scope the CSS to the demo: nest the stylesheet under its root class (@keyframes outside); a prefix is not enough. Hosts styling prose by class exclude stages with :not(:where(.ss-stage *)).
  • compact is a second design for the phone column, not a squeeze (framing.md, Small formats and the page).
  • Check a fresh load at each width and a resize between: targets, clips, overlays, poster.
  • A grid of cards is planned with bento.md.