Engineering Journal
Pdf Processor
Pdf Processor

Postmortem: Four Discarded Plans for Editing Structured HTML in Place

2026-05-21

TLDR

Building an in-place WYSIWYG block editor on top of structured HTML requires navigating browser collisions between contenteditable, CSS Grid layout rules, and HTML5 drag events. Four separate implementation iterations were discarded before arriving at a stable architecture: right-click intercept context menus, double-click text selection overrides, inline DOM drag handles, and global Monaco window bundle dependencies.
Implementation IterationArchitectural AssumptionBrowser CollisionResulting Fix
Plan 1: Right-Click InterceptContext menu handles edit triggersOverwrote image insertion menuIntegrated actions into existing menu
Plan 2: Double-Click TriggerDouble-click triggers edit dialogCollided with contenteditable text selectionRemoved double-click trigger
Plan 3: Flow Drag HandlesDrag handles prepended as HTML <span>Became CSS Grid items, breaking layoutSwitched to position: absolute handles
Plan 4: Monaco Global Windowwindow.monaco available globallyVite ES bundler tree-shook window globalImported monaco-editor ES module

Technical lessons & retrospective findings

1. CSS grid layout isolation

Prepending inline DOM handles as flow children inside CSS Grid containers causes handles to act as grid items, altering document layout during editing passes.

Remediation: Apply position: relative to grid containers and position drag handles using position: absolute:

/ Maintain identical visual layout across Edit and Selection modes /
.pdf-zone, .pdf-region {
  position: relative;
}

.pdf-drag-handle { position: absolute; top: 4px; left: 4px; z-index: 100; }


2. Browser event behavior mapping

contenteditable surfaces manage word selection, cursor focus, and input bubbling natively. Custom drag-and-drop overlays must toggle contentEditable = "false" during selection and drag modes to prevent event collisions.
export function setEditorSelectionMode(containerElement, isSelectionActive) {
  containerElement.classList.toggle('selection-mode', isSelectionActive);
  // Disable contentEditable during drag mode to prevent native selection collisions
  containerElement.contentEditable = isSelectionActive ? 'false' : 'true';
}
Rule of thumb: Map native browser behaviors (contenteditable, CSS Grid item auto-placement) before building interactive DOM overlay layers.
Read this post in the full Engineering Journal →