Skip to content

Derive editor presentation from containment, inert runtime, and block chrome #1483

Description

@chubes4

Problem

Editor presentation is assembled as a chain of symptom patches rather than a model of how an editor canvas differs from the frontend. NavigationStyleProjector::materializeEditorStaticStateStylesheet() currently holds six unrelated concerns, each guarded by its own ad-hoc condition, and each written with !important:

if ( '' !== $anchorProjectionCss ) { ... }                          // anchor projection
if ( preg_match('/animation.../', $authorCss) ) { ... }             // freeze animations
if ( $runtimeBehavior->emptyRuntimeTargetGenerated() ) { ... }      // dynamic-content placeholder
if ( $runtimeBehavior->emptyVisualGroupGenerated() ) { ... }        // painted-layer placeholder
if ( preg_match('/\bbody\b[^{}]*\{[^}]*overflow.../', $authorCss) ) // body overflow repair
foreach ( $styleResolver->closedStateRepairCssRules() ... )         // closed-state repair

Two consequences:

  1. Coverage is limited to symptoms already discovered. Anything not yet reported stays misplaced, because there is no model that would predict it.
  2. Decisions are made by sniffing author CSS with regular expressions (for example matching body { ... overflow ... } as text) rather than by consulting the site plan. That is fragile against equivalent-but-differently-written source CSS.

The method also lives in a class named for navigation while owning none of these concerns.

Expected contract

An editor canvas differs from the rendered frontend in exactly three ways. Derive the generated editor stylesheet from those, and have every emitted rule declare which one it addresses.

  1. Containment — the canvas is a bounded, non-viewport container. Geometry expressed against the viewport does not track it. This is the same class of defect as viewport-anchored media geometry: a value that is correct only while its container happens to match the viewport.
  2. Inert runtime — scripts are absent, so any runtime-driven state (entrance animation, open/closed disclosure, dynamically populated region) must resolve to a defined static state.
  3. Block chrome — the editor injects wrappers, placeholders, and variation pickers that the source composition never contained, and which occupy layout the source did not allocate.

Inputs should come from the site plan and the transform's own recorded behaviour, so the emitter reasons about known facts instead of re-detecting them from stylesheet text.

Acceptance

  • Every generated editor rule is attributable to containment, inert runtime, or block chrome.
  • Editor decisions read recorded transform/site-plan state rather than pattern-matching author CSS text.
  • Editor projection lives in a class that owns editor presentation, not in the navigation projector.
  • Rules stop relying on !important to defeat author CSS, except where a source declaration is itself !important.
  • The 292 parity fixtures pass unchanged.
  • A generated site opens in the editor with source composition intact: no injected picker occupying source layout, and no element positioned by a viewport-anchored value inside the bounded canvas.

Related

AI assistance disclosure

This issue was researched and written with AI assistance: Claude Sonnet 4.5 running in the opencode CLI agent. The AI read the editor stylesheet assembly path, enumerated the six concerns and the regex-driven conditions quoted above, and drafted the proposed three-axis contract. A human reviewed and directed the simplification framing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions