Viewport Surface Layer (Canvas Overlay)
The Viewport Surface Layer is a transparent HTML5 Canvas overlaid on top of the Shadow DOM. It draws all visual interaction affordances — selection outlines, resize handles, spacing adjusters, alignment guides, and layout badges.
How It Works
Managed by OverlayRenderer (src/renderer.ts), the Canvas overlay:
- Operates in canvas-space (world coordinates), not screen-space
- Uses the 2D Canvas API for high-performance vector drawing
- Scales and pans in perfect coordination with the Shadow DOM layer
- Redraws are throttled via
requestAnimationFrameto prevent layout thrashing
What Gets Drawn
Selection & Hover
| Visual | Description |
|---|---|
| Selection outline | Blue border around selected nodes |
| Hover outline | Light border when hovering over a node |
| Multi-selection | Outlines around all nodes in the selection set |
| Marquee selection | Dashed rectangle when dragging to select multiple nodes |
Resize Handles
Eight grab handles (NW, N, NE, E, SE, S, SW, W) appear on the selected node. Dragging these handles resizes the node wrapper, triggering a browser reflow.
Spacing Adjusters
When a single node is selected, hovering near its edges reveals:
- Padding adjusters — Inside the content boundary, shown in green
- Margin adjusters — Outside the content boundary, shown in orange
Dragging these adjusters modifies CSS padding/margin in real-time with live numeric tooltips.
Layout & Script Indicators
- Layout badges — Small labels showing the display mode (flex, grid, block) on container nodes.
- Script badges — A specialized
⚡️ JSbadge (drawn in amber) that appears on selected elements that have been explicitly marked by the host as containing JavaScript logic viaws.markNodeHasJS(nodeId). - Side-by-side badges — When a container has both layout rules and a JavaScript mark, both badges render side-by-side automatically.
- Grid tracks — Visualized column and row lines for CSS Grid containers.
- Alignment guides — Snap lines that appear when dragging nodes near alignment edges.
Drop Zone Indicators
During drag-and-drop operations:
- Insertion lines — Horizontal or vertical guides showing where a node will land
- Container highlights — Visual feedback when hovering over a valid drop target
Coordinate Space
The Canvas operates in canvas-space (world coordinates). All drawing uses coordinates transformed through the viewport matrix:
// The viewport matrix tracks zoom and pan
interface ViewportMatrix {
scale: number // Zoom level (0.1 to 4.0)
offsetX: number // Horizontal pan offset
offsetY: number // Vertical pan offset
}All pointer events are converted from screen-space to canvas-space using the screenToCanvas() function before being processed by the interaction engine.
Customizing the Overlay
The visual appearance of the overlay is configurable via OverlayStyle:
const ws = new Workspace(container, {
overlayStyle: {
selectionStroke: '#6366f1', // Selection border color
selectionGlow: 'rgba(99, 102, 241, 0.4)', // Glow effect
hoverStroke: 'rgba(99, 102, 241, 0.6)', // Hover border
guideStroke: '#f43f5e', // Snap guide line color
handleFill: '#ffffff', // Resize handle fill
handleStroke: '#6366f1', // Resize handle border
handleSize: 8, // Handle square size
layoutBadgeBg: 'rgba(99, 102, 241, 0.85)', // Layout badge background
layoutBadgeText: '#ffffff', // Layout badge text
dropZoneStroke: '#3b82f6', // Drop zone border
},
})Script Flagging Example
To render a JS badge for a node, call markNodeHasJS on the workspace:
// Explicitly flag the node as having JS behavior
ws.markNodeHasJS('element-id')
// Check if a node is flagged
if (ws.hasJSMark('element-id')) {
console.log('Node is marked with JS')
}
// Remove the flag
ws.unmarkNodeHasJS('element-id')