ui screenshots
ui-screenshots
Capture screenshots of web apps during development using Playwright and PIL. Supports full-page captures, interactive states, and an iterate-on-crop workflow that avoids slow re-screenshots.
Introduction source: author · github · b6483db44fb602654bca6a097185fcaddb305f97 Reference
Author's original introduction
Capture screenshots of web apps during development using Playwright and PIL. Supports full-page captures, interactive states, and an iterate-on-crop workflow that avoids slow re-screenshots.
SkillPi interpretation
- Tasks
- ui screenshots
- Audience
- Use the scenarios stated in the author's introduction.
- Inputs and outputs
- See the complete author document; outputs have not been runtime verified by SkillPi.
- Software, accounts and paid services
- Check required software, accounts, costs and permissions in the full document; confirm and authorize actual file access and external services in your host. Domains referenced in the documents (not a runtime allowlist): localhost The before/after examples include git checkout HEAD~1 -- <files>, which can replace uncommitted changes. Check git status, commit or back up changes, and confirm target files first. Check captures for sensitive information before sharing. Pin external tools from official sources and audit them separately; SkillPi has not installed or run those dependencies. The user must authorize file changes, external transfers, secrets and costs in the host tool.
- Limits and differences
- Functionality, version status and outcome statements in the introduction are author claims. Only pinned source and static attachment evidence were checked; runtime behavior, external services and compatibility are not guaranteed.
- Testing
- Not tested
Permissions and checks before use
Automatically summarized from this main document. Matches may be examples or defensive advice; they are not actual permission declarations. Attachments, external dependencies and runtime behavior are not covered.
-
Read local files
Limit access to selected files or the project directory. Avoid unrelated private records, secrets and configuration files.
Mentioned in main document · lines 54 -
Save or modify files
Confirm the output directory and whether existing files will be replaced. Back up important files and prefer a new output directory.
Mentioned in main document · lines 56, 93, 94, 153 -
Overwrite or clean a working tree
May replace uncommitted files or remove untracked files. Check git status, commit or back up changes, and confirm target files first.
Mentioned in main document · lines 101 -
Install or fetch external dependencies
Use official sources and pin versions. Check install scripts in an isolated environment. This document check does not cover code downloaded later.
Mentioned in main document · lines 22, 160 -
Screenshots or screen capture
Capture only a selected window or region. Avoid secrets and private records and review captures before sharing.
Mentioned in main document · lines 39, 76, 85, 150, 181
Domains or addresses referenced in the document
These addresses may be documentation links or examples, not an approved network allowlist. Verify actual destinations and outgoing data before use.
- localhost
Security rating and access scope
caution · Static evidence review; not a runtime safety guarantee.
Check required software, accounts, costs and permissions in the full document; confirm and authorize actual file access and external services in your host. Domains referenced in the documents (not a runtime allowlist): localhost The before/after examples include git checkout HEAD~1 -- <files>, which can replace uncommitted changes. Check git status, commit or back up changes, and confirm target files first. Check captures for sensitive information before sharing. Pin external tools from official sources and audit them separately; SkillPi has not installed or run those dependencies. The user must authorize file changes, external transfers, secrets and costs in the host tool.
Before use, confirm and authorize actual file access, external services and network destinations in your host. This review grants no runtime permissions.
Original SKILL.md
---
name: ui-screenshots
description: 'Capture screenshots of web apps during development using Playwright and PIL. Supports full-page captures, interactive states, and an iterate-on-crop workflow that avoids slow re-screenshots.'
---
# UI Screenshots
Capture screenshots of web apps and graphical UIs during development to document visual changes.
## When to Use This Skill
Use this skill when you need to:
- Capture the current state of a running web app
- Document a UI before and after a code change
- Screenshot interactive states (tooltips, hovers, selected elements)
- Capture specific sections of a page without re-screenshotting
## Prerequisites
```bash
pip install playwright Pillow -q
playwright install chromium
```
## Core Workflow
### 1. Take a raw full-page screenshot
```python
from playwright.async_api import async_playwright
async def capture(url="http://localhost:3000", out="screenshot-raw.png", width=1400, height=5000):
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(viewport={"width": width, "height": height})
await page.goto(url, wait_until="networkidle")
await page.wait_for_timeout(4000) # let charts/animations render
await page.screenshot(path=out, full_page=True)
await browser.close()
```
- Use a **tall viewport** (height=5000) so the page renders everything without scrolling
- `wait_until="networkidle"` + `wait_for_timeout(4000)` ensures async charts load
- `full_page=True` captures the entire scrollable content
### 2. View the raw image, then crop with PIL
**Do NOT try to get perfect crops via Playwright's `clip` parameter.** It's unreliable with full-page captures.
```python
from PIL import Image
img = Image.open("screenshot-raw.png")
cropped = img.crop((left, top, right, bottom)) # adjust based on what you see
cropped.save("screenshot-final.png")
```
1. Take the raw screenshot
2. View it to see actual pixel positions
3. Crop with PIL based on what you see
4. View the result — if not right, re-crop (instant, no re-screenshot needed)
### 3. Iterate on crop, not on capture
- Re-screenshotting is slow (browser launch + page load + render wait)
- Re-cropping is instant (just PIL)
- Get one good raw capture, then slice it as many ways as needed
### 4. Interactive states
```python
element = page.locator("selector").first
await element.hover()
await page.wait_for_timeout(1000) # let tooltip appear
await page.screenshot(path="screenshot-hover.png", full_page=True)
```
For "selected" state without hover effect, move the mouse away after clicking:
```python
await element.click()
await page.mouse.move(300, 300) # move away so hover doesn't show
await page.wait_for_timeout(500)
await page.screenshot(path="screenshot-selected.png", full_page=True)
```
### 5. Section-specific captures
Crop different sections from a single full-page screenshot:
```python
img.crop((0, 200, 920, 900)).save("screenshot-header.png")
img.crop((0, 900, 920, 1600)).save("screenshot-main.png")
```
## Guidelines
1. **Always capture before state BEFORE making any changes** — if you forget, you have to revert code to get a before shot
2. **Before/after pairs must use the same viewport width and crop** — otherwise the comparison is useless
3. **To get a "before" after you already changed code**: use `git checkout HEAD~1 -- <files>` to revert, screenshot, then `git checkout HEAD -- <files>` to restore
4. **For interactive states**: capture before AND after for each state — don't assume the "normal" before covers all cases
5. **Use `device_scale_factor=1`** in Playwright to force 1x pixels so screenshots match what users see at 100% zoom
6. **Charts need extra wait time** — Plotly, D3, etc. render asynchronously; 4s minimum after networkidle
7. **Narrow viewport reveals rendering bugs** — some border/alignment issues only appear at specific widths
## Non-Web App Screenshots
For desktop apps (VS, WPF, WinForms, console apps, terminals) where Playwright can't reach.
### mss + ctypes (recommended for desktop windows)
Find a window by title via Win32 API, capture its region with `mss`. Tested at ~33ms per capture.
```python
import ctypes
from ctypes import c_int, Structure, byref, windll
import mss
from PIL import Image
user32 = windll.user32
def find_window(title_contains):
"""Find visible windows matching a title substring."""
results = []
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_void_p)
def cb(hwnd, _):
if user32.IsWindowVisible(hwnd):
buf = ctypes.create_unicode_buffer(256)
user32.GetWindowTextW(hwnd, buf, 256)
if title_contains.lower() in buf.value.lower():
results.append((hwnd, buf.value))
return True
user32.EnumWindows(WNDENUMPROC(cb), 0)
return results
def capture_window(title_contains, output_path):
"""Capture a window by title substring."""
windows = find_window(title_contains)
if not windows:
raise ValueError(f"No window matching '{title_contains}'")
hwnd = windows[0][0]
class RECT(Structure):
_fields_ = [('left', c_int), ('top', c_int), ('right', c_int), ('bottom', c_int)]
rect = RECT()
user32.GetWindowRect(hwnd, byref(rect))
w, h = rect.right - rect.left, rect.bottom - rect.top
with mss.mss() as sct:
shot = sct.grab({'left': rect.left, 'top': rect.top, 'width': w, 'height': h})
img = Image.frombytes('RGB', shot.size, shot.rgb)
img.save(output_path)
return img
# Usage:
capture_window('Visual Studio Code', 'vscode-capture.png')
```
**Prerequisites:** `pip install mss pillow`
**Limitation:** Window must be visible (not behind other windows or minimized).
### Electron apps (VS Code, etc.)
**Node.js Playwright only** — Python Playwright has no `electron` API. Captures via CDP (Chrome DevTools Protocol), not from the screen — works even while minimized.
```javascript
const { _electron: electron } = require('playwright');
const app = await electron.launch({
executablePath: 'C:\\Program Files\\Microsoft VS Code\\Code.exe',
args: ['--new-window', '--disable-extensions', '--user-data-dir=' + tmpDir]
});
const window = await app.firstWindow();
await window.waitForLoadState('domcontentloaded');
// Minimize immediately — captures still work via CDP
await app.evaluate(({ BrowserWindow }) => {
BrowserWindow.getAllWindows()[0].minimize();
});
await window.screenshot({ path: 'capture.png' }); // works while minimized!
await app.close();
```
**Critical**: `--user-data-dir=<temp>` is required or VS Code hands off to the existing instance and the launched process exits immediately.
### Decision tree
| Scenario | Tool | Notes |
|---|---|---|
| Web app (localhost) | Playwright | Proven, full DOM access |
| Electron app (VS Code) | Playwright Electron (Node.js) | Works minimized via CDP |
| Desktop app, visible window | mss + ctypes (find by title) | ~33ms per capture |
| Desktop app, behind windows | Windows Graphics Capture API | Complex setup, Win10 1903+ |
| Quick full-screen | mss | ~68ms |
## Limitations
- Web capture requires a locally running app or accessible URL
- Desktop capture (mss) requires the window to be visible and unobstructed
- Electron capture requires Node.js Playwright (not Python)
- Some SPAs with heavy client-side rendering may need custom wait logic beyond networkidle
Copyright and license notice
MIT License Copyright GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
User discussion