| name | screenshot |
| description | Capture screenshots of web pages at multiple viewports for visual validation. Use this skill when you need to verify responsive layout or take before/after screenshots of UI changes. |
| user-invocable | true |
| allowed-tools | Bash, Read |
Screenshot Skill
Capture web pages at 6 viewports using Playwright (headless Firefox).
Script
uv run .claude/skills/screenshot/screenshot.py <url> [options]
Run from the project root.
Common Usage
uv run .claude/skills/screenshot/screenshot.py http://localhost:3123/commits --pass secret
uv run .claude/skills/screenshot/screenshot.py --all http://localhost:3123 --pass secret
uv run .claude/skills/screenshot/screenshot.py http://localhost:3123/commits --viewport mobile
uv run .claude/skills/screenshot/screenshot.py http://localhost:3123/files
uv run .claude/skills/screenshot/screenshot.py http://localhost:3123/files --output /tmp/shots
Auth
The script uses cookie-based auth via the /login form:
--pass secret → navigates to /login, fills the password, submits. The session cookie is captured and reused for all screenshots in that viewport context.
- No
--pass → no auth. For local-only mode where DASHBOARD_PASS is not set.
Pages (--all)
When using --all, these pages are captured:
| Page | Path |
|---|
| Files | /files |
| Commits | /commits |
| Terminal | /terminal |
| Notes | /notes |
| Overview | /overview |
| Performance | /performance |
| Logs | /logs |
Viewports
| Name | Size |
|---|
| desktop | 1200x800 |
| tablet | 768x1024 |
| mobile | 375x667 |
| mobile-large | 414x896 |
| tablet-landscape | 1024x768 |
| 4k | 1920x1080 |
Output
Screenshots go to ./screenshots/ (relative to cwd), named <page>-<viewport>.png.
Validation Workflow
- Start the web server
- Run screenshots:
uv run .claude/skills/screenshot/screenshot.py --all <base-url> --pass <pass>
- Read the PNG files to visually verify layout at each viewport
- Check for: broken layouts, overlapping elements, missing content, chart rendering
Interactive Screenshots
The CLI captures static pages. For interactive UI — modals, command palettes, dropdowns, hover states — use inline Playwright Python instead.
When to use inline Playwright
- You need to click an element before screenshotting (e.g. open a modal or palette)
- You need to type into an input (e.g. search query, form field)
- You need to wait for dynamic content (animations, search results, lazy-loaded data)
- You need to capture a specific transient state that the CLI can't reach
Invocation pattern
uv run --with playwright python3 -c "
from playwright.sync_api import sync_playwright
import time
...
"
Complete example: screenshot a modal with login
uv run --with playwright python3 -c "
from playwright.sync_api import sync_playwright
from pathlib import Path
URL = 'http://localhost:3123'
PASS = 'secret'
OUTPUT_DIR = Path('screenshots')
OUTPUT_DIR.mkdir(exist_ok=True)
VIEWPORTS = {
'desktop': {'width': 1200, 'height': 800},
'mobile': {'width': 375, 'height': 667},
}
with sync_playwright() as p:
browser = p.firefox.launch(headless=True)
for name, size in VIEWPORTS.items():
context = browser.new_context(viewport=size)
page = context.new_page()
# Login via cookie auth
page.goto(f'{URL}/login', wait_until='networkidle')
page.fill('input[name=\"password\"]', PASS)
page.click('button[type=\"submit\"]')
page.wait_for_load_state('networkidle')
# Navigate and interact
page.goto(f'{URL}/commits', wait_until='networkidle')
page.click('#folder-picker-btn') # open a modal
import time; time.sleep(0.3) # wait for animation
page.screenshot(path=str(OUTPUT_DIR / f'modal-{name}.png'), full_page=True)
context.close()
browser.close()
print(f'Done. Screenshots in {OUTPUT_DIR}/')
"
Key Playwright methods
| Method | Purpose |
|---|
page.goto(url, wait_until='networkidle') | Navigate and wait for load |
page.click(selector) | Click an element (CSS selector) |
page.fill(selector, text) | Clear an input and type text |
page.hover(selector) | Hover over an element |
page.keyboard.press('Escape') | Press a key |
page.wait_for_selector(selector) | Wait for element to appear |
time.sleep(seconds) | Wait for animations / async rendering |
page.screenshot(path=..., full_page=True) | Capture the screenshot |
Tips
- Viewport choice depends on intent: for design validation (layout, responsive CSS), loop through all 6 viewports. For debugging functionality (verifying a modal opens, data loads, a button works), a single viewport is enough — default to mobile (375x667) since the app is primarily used on mobile.
- Create a new context per viewport — this sets the viewport size cleanly (resizing an existing page can leave stale layout)
- Use
time.sleep() generously after interactions — headless Firefox needs time for CSS transitions and JS-driven UI updates
- Output naming: use
<feature>-<viewport>.png (e.g. modal-desktop.png) so files sort nicely alongside CLI-generated screenshots
Prerequisites
Install Playwright Firefox (one-time):
uv run --with playwright playwright install firefox