UX Audit Skill
You are a world-class UX designer and QA engineer. Your job is to walk through a user journey on a product using a browser session, take screenshots at every meaningful step, and produce a structured UX report with a complete fix list.
Mode Selection
Local mode — use when the product requires auth/cookies (your real logged-in session):
- Controls your actual Chrome via the ThinkRun extension + native host
- All commands operate on the tab you attach to
- Best for: apps behind login, staging environments with your cookies
Cloud mode — use for public-facing URLs, headless scraping, no auth needed:
- Provisions an isolated cloud browser
- Driven with
thinkrun cloud start+--mode cloud(see the web-browse skill) - Best for: marketing pages, public flows, pre-auth journeys
Step 0 — Configure the Audit
PRODUCT="<product name>"
PRODUCT_URL="<http://localhost:PORT or https://your-app.example.com>"
AUDIT_DATE=$(date +%Y-%m-%d)
JOURNEY="<one-line description of the journey to test>"
If testing against localhost, check what's actually on the port before assuming the right app is there — other services may be squatting the default port:
PORT=$(printf '%s\n' "$PRODUCT_URL" | sed -nE 's#.*:([0-9]+).*#\1#p')
[ -n "$PORT" ] && lsof -iTCP:"$PORT" -sTCP:LISTEN 2>/dev/null | head -3
Step 1 — Start Session
Local Mode (auth required)
# List available Chrome tabs
thinkrun tabs
# Attach to an existing tab (use a clean one — New Tab, or any non-critical tab)
# TAB_ID comes from the tabs output above
TAB_ID=656847550
thinkrun attach $TAB_ID
# Navigate to the product
thinkrun navigate "$PRODUCT_URL"
sleep 2
The attached tab is now the active session. All subsequent commands route to it automatically — no session ID needed.
Cloud Mode (no auth required)
# Start an isolated cloud browser; it becomes the active session.
thinkrun cloud start
# Subsequent commands run against it. Force cloud per-command with --mode cloud.
thinkrun navigate "$PRODUCT_URL" --mode cloud
# When done: thinkrun cloud stop
Step 2 — Helper Functions (Local Mode)
# Navigate and wait for page load
nav() {
thinkrun navigate "$1"
sleep 2
}
# Screenshot — saves to /tmp, read the path with the Read tool
shot() {
local label="$1"
local result=$(thinkrun screenshot --output "/tmp/ux_${label}.png")
echo "Screenshot: /tmp/ux_${label}.png"
# Then use Read tool on that path to observe the image
}
# Click by CSS selector
click_sel() {
thinkrun click "$1"
sleep 1
}
# Click by visible button/link text (safe evaluate — avoids ambiguous selectors)
click_text() {
thinkrun evaluate "[...document.querySelectorAll('button,a,[role=button]')].find(el=>el.textContent.trim()==='$1')?.click()"
sleep 1
}
# Type into a React-controlled input (triggers onChange via keyboard events)
# Use `type` for React apps, `fill` for plain HTML inputs
type_react() {
local selector="$1"
local value="$2"
thinkrun click "$selector"
thinkrun type "$selector" "$value"
}
# Get current URL
current_url() {
thinkrun url
# Returns: {"data": "http://..."} — data is a plain string
}
# Scroll the page
scroll_down() {
thinkrun scroll --down "${1:-500}"
}
scroll_up() {
thinkrun scroll --up "${1:-500}"
}
# Extract visible text
read_text() {
thinkrun evaluate "([...document.querySelectorAll('h1,h2,h3,p,label,span')].map(e=>e.textContent.trim()).filter(t=>t.length>4).join('\n')).slice(0,2000)"
}
Step 3 — Walk the Journey
For each step:
- Perform the action (navigate, click, type, scroll)
- Wait for state to settle (
sleep 1orsleep 2) - Take a screenshot:
thinkrun screenshot --output "/tmp/ux_NN_label.png" - Read the screenshot file with the Read tool to observe it
- Note: what you see vs what you expected
Standard Journey Template
# 1. Landing / Homepage
thinkrun navigate "$PRODUCT_URL"
sleep 2
thinkrun screenshot --output "/tmp/ux_01_homepage.png"
# → Read tool: /tmp/ux_01_homepage.png
# Observe: CTA clarity, value proposition, onboarding path
# 2. Auth / Login (if required)
thinkrun navigate "${PRODUCT_URL}/login"
sleep 1
thinkrun screenshot --output "/tmp/ux_02_login.png"
# → Read tool: /tmp/ux_02_login.png
# Fill login form (use type for React-controlled inputs, fill for plain HTML)
thinkrun click "input[type=email]"
thinkrun type "input[type=email]" "test@example.com"
thinkrun click "input[type=password]"
thinkrun type "input[type=password]" "testpassword"
thinkrun click "button[type=submit]"
sleep 3
thinkrun screenshot --output "/tmp/ux_03_post_login.png"
# → Read tool: /tmp/ux_03_post_login.png
# Check: Did login succeed? What URL are we on?
thinkrun url
# 3. Core Action
thinkrun screenshot --output "/tmp/ux_04_core_action_start.png"
# ... perform the action ...
sleep 2
thinkrun screenshot --output "/tmp/ux_05_core_action_result.png"
# 4. Scroll to see full content
thinkrun scroll --down 600
thinkrun screenshot --output "/tmp/ux_06_scrolled.png"
# 5. Error Path (submit empty form, invalid input, etc.)
# navigate back, try invalid actions...
thinkrun screenshot --output "/tmp/ux_07_error_state.png"
# 6. Accessibility snapshot (reveals element structure, roles, text)
thinkrun snapshot
Step 4 — Observation Framework
At each screenshot, read the image and score against these dimensions:
| Dimension | Questions to Ask |
|---|---|
| Clarity | Is it immediately obvious what to do next? |
| Feedback | Does the UI communicate what's happening? |
| Progress | Can the user tell how far along they are? |
| Output | Is the result visible and readable? |
| Error visibility | Are failures explained clearly, with a recovery path? |
| Navigation | Easy to go back, forward, or to related sections? |
| Performance feel | Does it feel fast or sluggish? Is there a loading indicator? |
| Empty states | What does a new user see before any data exists? |
| Mobile | Does the layout hold at 375px? |
Rate each: ✅ Good / 🟡 Needs work / 🔴 Broken
Step 5 — CLI DX Checklist (if product has a CLI)
PRODUCT_CLI="<cli-binary>" # leave empty to skip
if [ -n "$PRODUCT_CLI" ]; then
$PRODUCT_CLI --help
$PRODUCT_CLI --version
fi
Step 6 — Report Format
## UX Audit: <PRODUCT> — <YYYY-MM-DD>
### Journey Tested
<one-line description>
### Journey Map
| Step | Expected | Actual | Status |
|------|----------|--------|--------|
| 1. Homepage | Clear CTA | ... | ✅/🟡/🔴 |
### Critical Issues 🔴
- **Issue title**: Description. Screenshot: `ux_07_error_state.png`. Impact: ...
### Moderate Issues 🟡
- **Issue title**: Description. Screenshot: `ux_03_post_login.png`. Impact: ...
### What Works Well ✅
- Feature and why it works well.
### Scores by Dimension
| Dimension | Score | Notes |
|-----------|-------|-------|
| Clarity | ✅ | ... |
| Feedback | 🔴 | ... |
### Top 3 Fixes (Priority Order)
1. **Fix title** — rationale and expected impact
2. **Fix title** — rationale and expected impact
3. **Fix title** — rationale and expected impact
Deliver the report wherever your team tracks findings — a Markdown file in the repo, an issue per critical finding, or your task tracker. Keep the screenshot files referenced by the report so findings stay verifiable.
Known Gotchas
filldoesn't trigger ReactonChange— usetypeinstead for React-controlled inputs;fillworks for plain HTML inputs- Screenshot returns a local file path —
{"data": {"path": "/tmp/thinkrun-xxx.png"}}. Use the Read tool on that path to view it. There is no--labelflag. thinkrun urlreturns a plain string —{"data": "http://..."}not{"data": {"url": "..."}}. Parse asd['data'].- Scroll syntax —
thinkrun scroll --down 500(notscroll down 500) - Never use a generic
buttonselector when multiple buttons exist — usebutton[type=submit]orclick_texthelper evaluatecan timeout — keep scripts short. Avoiddocument.body.innerTexton large SPAs; use targeted selectors instead- CSRF-protected forms — auth forms need CSRF token. If the app fetches one after load, the fetch must complete before submit. Use
sleep 2after navigation before filling. - Tab becoming unresponsive — if commands timeout, use
thinkrun tabsto find a responsive tab, thenthinkrun attach <newTabId>to switch switch-tab≠attach—switch-tabchanges Chrome's active tab for visual focus;attachchanges which tab receives CLI commands. You need both when switching tabs.- Cloud mode screenshot —
thinkrun screenshot --output /tmp/sc.png --mode cloudsaves the file directly; read the path with the Read tool