Appearance
Builder Rulers: Improvements, Grid Overlay & Alignment Guides
Branch: add-builder-ruler Date: 2026-05-21
Background
The builder has a working ruler system (MultiRuler.vue) with 3 draggable rulers per axis and percentage-based snapping. This plan covers four areas of improvement:
- Ruler UX improvements (scrub inputs, design system alignment, cleanup)
- Grid overlay (pixel grid with adjustable density)
- Alignment guides (engine reports element bounding rects, frontend shows alignment lines)
- Element snap (rulers snap to actual block edges, not just fixed percentages)
The alignment guides and element snap features are designed to work with the upcoming live scrub-input values from the theming branch (CE PR #722), where users can manipulate block properties in real-time.
Scope
1. Ruler Improvements
Scrub inputs in control panel:
- Replace the static percentage display in RulerControlPanel with scrub-enabled InputField components
- Users can click the percentage label and scrub to set exact ruler position
- Consistent with the rest of the builder where all number inputs support scrub
Design system alignment:
- RulerControlPanel currently uses hardcoded colors (green buttons, dark background, custom border-radius)
- Restyle to match BrandingToolbar/LabelToolbar pattern:
$background-panel,$border-minus2,$border-radius-kilo - Ruler lines and handles should use CSS variables (
var(--text-primary),var(--accent-primary)) instead of hardcoded hex - Active/selected state should use
var(--accent-primary)instead of custom green
Cleanup:
- Delete
Ruler.vue(legacy, never imported) - Remove dead CSS class
.ruler-draggingin PreviewPanel - Remove unused
isRulerDraggingstate in PreviewPanel if confirmed dead
2. Grid Overlay
Pixel grid over iframe:
- Semi-transparent grid rendered as an overlay on top of the iframe (same layer as rulers, z-index 100)
- Default grid size: 8px (standard design convention)
- Slider in control panel adjusts grid density: 4px to 64px range
- Grid lines use very subtle styling:
rgba(var(--text-primary-rgb), 0.08)or similar - Toggle on/off via button in control panel
Implementation approach:
- Canvas element or CSS background pattern (repeating-linear-gradient) -- CSS is simpler and performs well for regular grids
- Sized to match the iframe dimensions, positioned identically to rulers
- Scales with panzoom (lives inside panZoomContainer)
- Grid state (enabled, size) persisted to localStorage alongside ruler state
3. Alignment Guides (Engine Cooperation)
Engine side (Creative-Engine):
- New postMessage type:
cavai-devtools-element-rects - Engine sends bounding rects for all visible blocks when requested or when layout changes
- Message payload:ts
{ type: 'cavai-devtools-element-rects', blocks: Array<{ shortcode: string // e.g. "t1", "b2", "g1" rect: { top: number, left: number, width: number, height: number } }> } - Exposed via
__cavaiDevtools.getElementRects()for on-demand queries - Optionally: engine sends rects automatically during live value scrubbing (so guides update in real-time)
Frontend side (Application-Frontend):
- New component:
AlignmentGuides.vue(sibling to MultiRuler, same container) - Listens for
cavai-devtools-element-rectsmessages - When a ruler is being dragged near a block edge, show a colored alignment line at that edge
- When live scrub values change block positions, show alignment lines between blocks that share an edge or center
Alignment detection:
- For each block, track: top, bottom, left, right, centerX, centerY
- Show guide when two blocks share a value within a threshold (e.g. 2px)
- Show distance indicator between nearby-but-not-aligned edges
Visual style:
- Alignment lines:
var(--accent-primary)with low opacity, 1px solid - Distance indicators: small label showing px distance, same accent color
- Guides disappear when drag/scrub ends (transient, not persistent)
4. Element Snap for Rulers
Extended snap points:
- In addition to the existing percentage snaps (0%, 25%, 33%, 50%, 66%, 75%, 100%), rulers also snap to block edges
- When engine reports element rects, convert block edges to percentages relative to creative dimensions
- Add these as dynamic snap points to the existing snap system in MultiRuler
- Magnetic pull behavior identical to existing percentage snaps
- Visual feedback: snap label shows block shortcode instead of percentage (e.g. "t1 top" instead of "25%")
Architecture
PreviewPanel.vue
|-- panZoomContainer
| |-- iframe (creative engine)
| |-- GridOverlay.vue (CSS grid pattern, toggleable)
| |-- MultiRuler.vue (existing, enhanced with element snap points)
| |-- AlignmentGuides.vue (transient lines during drag/scrub)
|
|-- RulerControlPanel.vue (restyled, scrub inputs, grid toggle + slider)
Engine (postMessage):
devtoolsBridge.ts --> cavai-devtools-element-rects
flowBridge.ts --> __cavaiDevtools.getElementRects()Implementation Order
Phase 1: Ruler cleanup and polish (frontend only)
- Delete
Ruler.vue(dead code) - Remove dead
isRulerDragging/.ruler-draggingreferences in PreviewPanel - Restyle RulerControlPanel to use design system tokens (BrandingToolbar pattern)
- Restyle MultiRuler lines and handles to use CSS variables
- Add scrub-input for ruler position in RulerControlPanel (replace static % label)
Phase 2: Grid overlay (frontend only)
- Create
GridOverlay.vue-- CSS repeating-linear-gradient, sized to iframe - Add grid toggle button and size slider to RulerControlPanel
- Persist grid state to localStorage
- Ensure grid scales correctly with panzoom
Phase 3: Engine element rect reporting (engine + frontend)
- Add
getElementRects()to engine's devtoolsBridge - Add
cavai-devtools-element-rectspostMessage type - Frontend: listen for element rects in PreviewPanel, store in reactive state
- Pass element rect data to MultiRuler as dynamic snap points
Phase 4: Alignment guides (frontend, depends on Phase 3)
- Create
AlignmentGuides.vue-- receives element rects, detects alignment - Show guide lines when blocks share edges/centers
- Show distance indicators between nearby edges
- Integrate with live scrub events (guides update during value manipulation)
Phase 5: Element snap for rulers (frontend, depends on Phase 3)
- Extend MultiRuler snap system with dynamic element-edge snap points
- Update snap labels to show block shortcode context
- Test with various creative layouts and block configurations
Dependencies
- Phase 1-2: No dependencies, can start immediately on
add-builder-rulerbranch - Phase 3-5: Depends on theming branch being merged (for live scrub infrastructure)
- Phase 3: Requires changes in Creative-Engine repo
Files to Touch
Frontend (Application-Frontend):
Preview/MultiRuler.vue-- element snap points, CSS variable stylingPreview/RulerControlPanel.vue-- scrub inputs, grid controls, design system restylePreview/PreviewPanel.vue-- element rect listener, dead code cleanupPreview/Ruler.vue-- DELETEPreview/GridOverlay.vue-- NEWPreview/AlignmentGuides.vue-- NEW
Engine (Creative-Engine):
src/devtools/devtoolsBridge.ts-- element rect reportingsrc/devtools/flowBridge.ts-- getElementRects() on __cavaiDevtoolssrc/devtools/types.ts-- new types
Notes
- Grid overlay uses CSS patterns (not canvas) for simplicity and performance
- Alignment guides are transient (visible during interaction only), not persistent like rulers
- Element rects from engine use block-local coordinates; frontend converts based on iframe position
- All new styling must use SCSS variables and CSS custom properties, no hardcoded colors
- Scrub inputs in control panel reuse the existing InputField component with scrub support
- Phase 1-2 can be done now; Phase 3-5 should wait until theming branch lands