Canvas Edit - Live Annotation Toolbar
Floating toolbar that overlays design review findings directly on web pages in real-time. Displays numbered badges on problematic elements with severity-colored borders, hover popovers for issue details, and one-click screenshot capture.
Key features:
- •Live annotations - Numbered badges appear on elements as issues are found
- •Severity indicators - Color-coded badges (red/orange/blue) with counts
- •Issue popovers - Click badges to see full issue details and recommendations
- •Screenshot capture - Capture annotated page (toolbar hidden, annotations visible)
- •Shadow DOM - Toolbar is invisible to agent-eyes screenshots
- •Filtering - Filter by severity or pillar category
Breaking Changes from v1
This is a complete redesign. Canvas-edit is now a viewing tool, not an editing tool.
| Old Functionality | New Behavior |
|---|---|
| Text editing via textarea | REMOVED |
| Style sliders (fontSize, etc.) | REMOVED |
| "Save All to Code" button | REPLACED with screenshot capture |
| contentEditable toggle | REMOVED |
edit command | REPLACED with inject command |
For live editing, use agent-canvas --with-edit instead.
Prerequisites
- •Python 3.10+
- •
uvpackage manager - •Playwright browsers:
playwright install chromium
Commands
SKILL_DIR=".claude/skills/canvas-edit/scripts"
Inject Annotations onto Page
# Inject toolbar with issues from JSON file
uv run $SKILL_DIR/canvas_edit.py inject http://localhost:3000 --issues issues.json
# Inject with issues from stdin
echo '[{"id": 1, "selector": "h1", "severity": "major", "title": "Contrast issue"}]' | \
uv run $SKILL_DIR/canvas_edit.py inject http://localhost:3000 --issues -
# Auto-screenshot on load
uv run $SKILL_DIR/canvas_edit.py inject http://localhost:3000 --issues issues.json --screenshot
Typical Workflow: Design Review + Annotations
# 1. Run design review to find issues uv run .claude/skills/design-review/scripts/design_review.py review http://localhost:3000 \ --output-json issues.json # 2. Inject annotations onto the page uv run $SKILL_DIR/canvas_edit.py inject http://localhost:3000 --issues issues.json # 3. User interacts with annotations, takes screenshots, closes browser
Toolbar Controls
The floating toolbar (top-right by default) provides:
Status Display
- •Issue count ("5 Issues" or "All looks good!")
- •Severity badges: 🔴 blocking, 🟡 major, 🔵 minor
Actions
- •👁 Visibility: Show/hide all annotations
- •⚙ Filter: Filter by severity or pillar category
- •📸 Screenshot: Capture page with annotations (toolbar hidden)
- •↕/↔ Orientation: Toggle vertical/horizontal toolbar
- •✕ Dismiss: Remove toolbar and all annotations
Dragging
- •Grab the ☰ handle to drag toolbar anywhere on screen
- •Position persists during session
Annotation Badges
Each issue appears as a numbered badge on its target element:
- •Position: Top-right of target element (auto-adjusts at screen edges)
- •Color: Border matches severity (red/orange/blue)
- •Click: Opens popover with full issue details
- •Hover: Highlights the target element
Badge Popover Contents
┌─────────────────────────────────────┐ │ #3 Contrast issue [major] │ ├─────────────────────────────────────┤ │ Color contrast ratio 3.2:1 fails │ │ WCAG AA requirement of 4.5:1 │ │ │ │ Pillar: Quality Craft │ │ Check: color-contrast │ ├─────────────────────────────────────┤ │ Recommendation: │ │ Change text color to #1a1a1a or │ │ background to #ffffff │ └─────────────────────────────────────┘
Issue JSON Format
Issues can come from design-review output or be manually constructed:
[
{
"id": 1,
"selector": ".hero-title",
"severity": "major",
"title": "Contrast ratio insufficient",
"description": "Text contrast 3.2:1 fails WCAG AA (4.5:1 required)",
"pillar": "Quality Craft",
"checkId": "color-contrast",
"recommendation": "Use darker text (#1a1a1a) or lighter background"
},
{
"id": 2,
"selector": "button.submit",
"severity": "minor",
"title": "Touch target too small",
"description": "Button is 36x28px, minimum is 44x44px",
"pillar": "Quality Craft",
"checkId": "touch-target-size"
}
]
Required Fields
| Field | Type | Description |
|---|---|---|
id | number | Unique identifier |
selector | string | CSS selector for target element |
severity | string | "blocking", "major", or "minor" |
title | string | Short issue title |
Optional Fields
| Field | Type | Description |
|---|---|---|
description | string | Detailed explanation |
pillar | string | Design pillar category |
checkId | string | Identifier for the check that found this |
recommendation | string | Suggested fix |
Event API (Canvas Bus)
Canvas-edit integrates with other skills via the canvas bus event system.
Events Emitted
| Event | Payload | When |
|---|---|---|
annotation.clicked | {issueId, selector, severity} | User clicks a badge |
screenshot.requested | {directory, filename, issueCount} | Screenshot button clicked |
screenshot.captured | {path, issueCount} | Screenshot saved (Python-side) |
annotations.cleared | {} | Dismiss button clicked |
filter.changed | {severity, pillars} | Filter settings changed |
Events Subscribed
| Event | Action |
|---|---|
review.started | Show "Scanning..." state |
review.issue_found | Add badge for new issue |
review.completed | Show final count or success message |
capture_mode.changed | Hide/show toolbar for agent-eyes |
Integration Example
// In another skill's JavaScript
const bus = window.__canvasBus;
// Listen for annotation clicks
bus.subscribe('annotation.clicked', (payload) => {
console.log(`Issue ${payload.issueId} clicked: ${payload.selector}`);
});
// Add an issue programmatically
window.__annotationLayer.addIssue({
id: 99,
selector: '.problematic-element',
severity: 'major',
title: 'New issue found'
});
Screenshot Output
Screenshots are saved to .canvas/screenshots/ with timestamp filenames:
.canvas/screenshots/ ├── 2026-01-23T15-30-45_5-issues.png └── 2026-01-23T15-45-12_0-issues.png
Filename format: YYYY-MM-DDTHH-MM-SS_N-issues.png
Screenshots capture:
- •Full page content
- •All visible annotation badges
- •Element highlights (if active)
- •NOT the toolbar (hidden during capture)
Toolbar States
| State | Display | Trigger |
|---|---|---|
| Issues Found | "N Issues" + severity badges | Default when issues > 0 |
| All Clear | "✓ All looks good!" (randomized) | Zero issues after review completes |
| Scanning | "⟳ Analyzing..." with spinner | During review.started |
Success messages rotate randomly:
- •"All looks good!"
- •"Ship it!"
- •"Pixel perfect"
- •"Zero issues found"
- •"Looking sharp!"
Keyboard Navigation
| Key | Action |
|---|---|
Tab | Navigate toolbar controls |
1-9 | Jump to badge by number |
Arrow keys | Navigate between visible badges |
Enter/Space | Activate focused button/badge |
Escape | Close open popover |
Shadow DOM Isolation
The toolbar is rendered inside a closed Shadow DOM:
- •Invisible to
document.querySelector() - •Excluded from agent-eyes DOM snapshots
- •Hidden from screenshots (annotations remain visible)
- •Page styles cannot affect toolbar appearance
Notes
- •Toolbar auto-repositions to stay on screen when dragged or resized
- •Badges reposition when window resizes or scrolls
- •Multiple badges on the same element stack with offset
- •Orphaned badges (element removed) are automatically cleaned up
- •Filter state persists during session but resets on page reload