| name | annotate-screenshot |
| description | Generates DOM-accurate red-rect + arrow annotations on audit screenshots. Use when the user asks to "annotate", "mark up", "add red boxes to" screenshots, or when finalizing a screen section of the audit. |
annotate-screenshot
Generates annotated copies of audit screenshots using scripts/annotate.py. Every annotation is driven by DOM coordinates — never eyeballed.
Preconditions
- Screenshots exist in
screenshots/ (viewport captures, same viewport the rects were measured at).
- Playwright MCP is connected (you'll need it to read the DOM rects).
.venv exists with Pillow installed.
Procedure
1. Gather DOM rects
For each element you want to mark, in the same viewport the screenshot was taken at:
const el = document.querySelector('<SELECTOR>');
const r = el.getBoundingClientRect();
({ x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) })
Record (x, y, w, h) per element. One rect per finding.
2. Write the JOB
Open scripts/annotate.py and append to JOBS at the bottom. Pattern:
def ann_<screen_id>(draw, size):
box = boxed(draw, (x, y, w, h))
arrow_to(draw, box, "left")
JOBS += [
("<NN-screen-id>-desktop.png", ann_<screen_id>),
]
3. Run
source .venv/bin/activate
python3 scripts/annotate.py
Output is written next to the source: foo.png → foo-annotated.png.
Rules (PLAYBOOK §11)
- Only red rectangles and red arrows. No text on the image.
- Pad 8 px around the DOM rect (4 px on mobile).
- Line width 4 px desktop, 3 px mobile, arrow 5 px.
- Arrow side: prefer
left for sidebar-placed elements, top or bottom for full-width. Choose whichever gives the arrow its own empty space to sit in.
- Never annotate empty space ("missing X" lives in the text, not on the image).
- Never annotate decorative illustrations (phone mockups, dashboard samples).
- If the finding is about multiple adjacent elements, draw one rect around both — don't stack two rects.
Validation
Before declaring the annotation done:
- Open the
-annotated.png file. Rect must visibly wrap the target element with ~8 px breathing room.
- Arrow must not overlap the rect or cross other UI elements.
- If the target is near the viewport edge, shrink the arrow length (
LEN constant) rather than clipping.
When coordinates drift
After a redeploy, DOM positions shift. Re-measure with getBoundingClientRect() before regenerating. Don't "fix" visually.