Core ConceptsViewport Surface Layer

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 requestAnimationFrame to prevent layout thrashing

What Gets Drawn

Selection & Hover

VisualDescription
Selection outlineBlue border around selected nodes
Hover outlineLight border when hovering over a node
Multi-selectionOutlines around all nodes in the selection set
Marquee selectionDashed 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 ⚡️ JS badge (drawn in amber) that appears on selected elements that have been explicitly marked by the host as containing JavaScript logic via ws.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')