Viewport (Canvas Area)

The middle column of the editor — the canvas where the page renders. This folder spells out how the canvas is structured, the device/breakpoint/zoom machinery, and the interactions that hang off it.

The middle column of the editor — the canvas where the page renders. This folder spells out how the canvas is structured, the device/breakpoint/zoom machinery, and the interactions that hang off it.

docs/ is gitignored — these notes are local-only.

What you see in the UI

┌──────────────────────────────────────────────────────────┐
│  ⋮  💾  📐  👁  [📺▼]  ↶ ↷  +    ← Header  (canvas mode + zoom)
├──────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────┐         │
│ │                                              │         │
│ │            <iframe-like page render>         │         │
│ │            (clamped/centered per mode)       │         │
│ │                                              │         │
│ └──────────────────────────────────────────────┘         │
└──────────────────────────────────────────────────────────┘

The canvas has three orthogonal modes that combine to produce what the user sees:

  1. View (ViewAtom): desktop, mobile, sm, md, lg, xl, 2xl. Determines width/clamp behavior AND the write scope (canvas viewport = edit scope). Default is device-awaredesktop (fluid full-width) when the editor loads on a viewport ≥ 768px, mobile when loading on a touch/narrow device. Both desktop and mobile write to the base Tailwind layer. Switching to md sets the canvas to 768 px and directs all class writes to md:. (Defined at the ViewAtom declaration; see atoms.ts. Full scope logic: responsive-editor.md.)
  2. Device (DeviceAtom): boolean. When true + view === "mobile", renders inside a phone bezel with width/height from DeviceDimensionsAtom.
  3. Zoom: persisted CSS zoom scale applied to the canvas wrapper. Two atoms — one for breakpoint/desktop view, one for device-mobile view.

Plus a fourth toggle:

  • Preview (PreviewAtom): hides editor chrome and disables CraftJS selection — pure visitor render.

Read these in order

  1. zoom.mdBreakpointZoomAtom / DeviceZoomAtom, CanvasZoom component, fit-to-window, persistence, the icon-vs-percentage trigger.

(Add more files here as we document other viewport areas — canvas modes, preview, header chrome, breadcrumb, etc.)

Source map (one-line index)

ConcernFile
Top header — canvas mode dropdown, undo/redo, preview, insertpackages/sdk/src/chrome/viewport/ViewportTopBar/
Canvas wrapper — applies view classes, device bezel, CSS zoompackages/sdk/src/chrome/viewport/Viewport/
Zoom UI primitive (+/−, dropdown, fit-to-window, reset)packages/sdk/src/chrome/viewport/CanvasZoom.tsx
Atoms — ViewAtom, DeviceAtom, *ZoomAtom, etc.packages/sdk/src/chrome/viewport/atoms.ts
Breakpoint pixel widths + isEditorCanvasBreakpointViewpackages/sdk/src/utils/tailwind/className.ts
Device dimensions / phone pickerpackages/sdk/src/chrome/viewport/DeviceSelector.tsx
Selected node breadcrumb (top of canvas in some modes)packages/sdk/src/chrome/viewport/NodeBreadcrumb.tsx
Right-click contextual menu on canvaspackages/sdk/src/chrome/viewport/ToolboxContextual/

Canvas modes — quick reference

ModeViewDeviceWidth behaviorCentered?Zoom atom
Responsive (default)desktopoffFluid — fills columnNo (flex-row)BreakpointZoomAtom
Breakpointsm2xloffClamped to fixed px widthYes (bg-neutral/50 letterbox)BreakpointZoomAtom
DevicemobileonPhone width + bezelYes (centered)DeviceZoomAtom

Switching is via the canvas-mode dropdown in the header. The dropdown trigger icon swaps based on view (desktop icon / mobile icon / SM-MD-LG-XL-2XL label), and when zoom ≠ 100% it shows the percentage instead — see zoom.md.

Related