Shoot polished, framed screenshots of the open-slide dev UI (editor, inspector, Arrange tab, comment mode, agent panel, assets manager, svgl search, themes, presenter, ⌘K menu, slide overview, export menu) as cropped webp images — the app as a rounded, shadowed window on a soft gray stage, with padding where the window edge shows and bleed where the crop cuts through. This is the house style for docs pages, the changelog, the landing site, and READMEs. Use it whenever the user asks for UI screenshots, docs or changelog images, "nice"/"framed"/"cropped" screenshots, or wants images under apps/web/public/assets refreshed because the UI changed — even if they don't name this skill or describe the style.
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
The output is a webp like a product-page detail shot: a crop of the real dev UI, rendered as a window with rounded corners and a layered shadow on a warm gray gradient. Window edges that fall inside the crop get 56px of padding; edges where the crop cuts through the UI bleed off the stage. That contrast is what makes a crop read as "a detail of a real app" instead of a pasted rectangle, so choose crops with it in mind.
Everything is captured from a real open-slide dev server running on a
throwaway scratch project, so the shots show the current UI, not a mock.
All scripts live in .claude/skills/ui-screenshots/scripts/ and are run from
the repo root with node.
open-slide dev, so screenshots need one. Explain
that and propose the throwaway scratch server below. Approval doesn't carry
over from an earlier session.@slide-comment markers and edits into it.Pick the shots. One image per idea. Show the state that explains the feature: a selected element with the panel open, a dialog with real results, pins already posted. For docs, the page's hero image usually goes right under the frontmatter, with section images under the heading they illustrate. For the changelog, only releases worth a picture get one.
Build the scratch project.
node .claude/skills/ui-screenshots/scripts/setup-scratch.mjsThis creates packages/core/e2e/.scratch/docs-shots (gitignored) with
eight demo decks, the getting-started template deck, a q2-launch deck
containing an <ImagePlaceholder>, the demo themes, brand-logo and
generated-gradient assets, and per-entry node_modules symlinks to
apps/demo. The symlinks are per entry so the dev server's
node_modules/.open-slide state stays inside the scratch copy.
Start the server in the background (run_in_background), then read
its output for the ready line:
cd packages/core/e2e/.scratch/docs-shots && OPEN_SLIDE_DEV_SUPERVISED=1 node ../../../bin.js dev --no-skills-check --host 127.0.0.1 --port 43219If it dies with Cannot find package …, node_modules has drifted from
the lockfile (common right after pulling a dependency bump). Run
pnpm install --frozen-lockfile from the repo root and start again. The
core dist/ must be built too (pnpm core build) if packages/core/src
changed since the last build.
Write a shots file in the scratchpad. Start from
scripts/shots.example.mjs, which has a working recipe for every state
listed in the description, plus the field reference at the top. Copy the
entries you need and adjust crops. Selectors there use the app's real
accessible names, so prefer them over guessing.
Shoot.
node .claude/skills/ui-screenshots/scripts/shoot.mjs <shots.mjs> --out <dir> [--only a,b]Each shot is captured at 3× device scale and composed at 2×, giving a
2000px-wide webp (q86, usually 40–100 KB). Raw captures are kept in
<scratch>/.raw/ for debugging.
Look at every image before using it. The Read tool can open webp files. To review a batch at once:
node .claude/skills/ui-screenshots/scripts/contact-sheet.mjs <sheet.png> a.webp b.webp c.webp d.webpCheck for: a crop that slices through a label or pill, empty canvas
dominating the frame, text too small to read, a stray hover state, a loading
state, odd search matches, the wrong page. Fix and reshoot with --only.
Place the images.
apps/web/public/assets/screenshots/<name>.webp, referenced from
MDX as . The docs map img to
fumadocs' ImageZoom, so they zoom on click automatically.apps/web/public/assets/<area>/, and
render them with ImageZoom from fumadocs-ui/components/image-zoom
(same props as next/image) so they zoom like the docs.git grep the name first).Check them in context without a dev server:
node .claude/skills/ui-screenshots/scripts/preview-web.mjs --route /changelog --out <dir> [--width 1440] [--dark] [--click "figure img"]This builds a static export of apps/web in a scratch copy, then
full-page-shoots the route and slices it into 2200px sections. Pass
--keep-docs to include /docs routes (slower build), --skip-build to
reuse the last export, and --click to confirm click-to-zoom.
Clean up. Stop the server by port and remove the scratch copies:
kill $(lsof -tiTCP:43219 -sTCP:LISTEN)
rm -rf packages/core/e2e/.scratch/docs-shots packages/core/e2e/.scratch/web-previewAn exit code of 143 from the background server task just means it was killed.
{ x: 300, y: 0, w: 980, h: 600 } shows the window's top and right edges and bleeds left and
bottom, which suits right-side panels. Make every crop touch at least one
viewport edge, or nothing in the frame shows a window edge.crop for an overview shot (editor, slide overview,
presenter). All four edges get padding.stage: 'dark' is only for
UIs that are dark themselves, like the presenter./__agent/sessions, /__agent/sessions/<id>, and /__agent/events, and
seed open-slide:agent-session / open-slide:agent-dock in localStorage.
Delay the mocked responses ~4s and open the deck with ?p=1. If a running
session is known before the deck loads, the follow-along logic jumps to the
last page.@slide-comment markers into
the scratch deck. That's fine, but shots taken later in the same run show a
comment badge on the toolbar. Run setup-scratch.mjs again for a clean
slate.Search by brand… placeholder (focus
starts on the svgl.app link). Wait for networkidle. Pick a colourful
brand like Figma, because monochrome logos render as an empty tile.context.waitForEvent('page'), so it shows as linked to
the main window.page.getByTitle('Arrange'). The Text/Image tab is the
default.1weiho/open-slide
9fae234
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.