Skip to content

fix(php-transformer): keep the source panel box on a projected modal dialog - #2744

Merged
chubes4 merged 3 commits into
trunkfrom
fix/projected-dialog-source-box
Oct 13, 2026
Merged

chubes4 merged 3 commits into
trunkfrom
fix/projected-dialog-source-box

Conversation

@ellenbauer

@ellenbauer ellenbauer commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Refs #2726

What was wrong

With #2719 and #2720, the Dunbar Oaks (Wix) phone menu opens, but the panel is white instead of the source's beige and its column sits about 14px right of the source. Both come from the projected dialog's own box, not from the navigation.

The menu panel #MENU_AS_CONTAINER is held twice in the compiled page. The capture's hidden wired copy (#dla-dialog-0 > #MENU_AS_CONTAINER) keeps the panel's id. The captured open state becomes the native <dialog> under a generated id (blocks-engine-dialog-<hash>), because the id is already taken. Two things follow:

  1. Id rules miss the dialog. Wix places and paints the panel through id rules: #MENU_AS_CONTAINER{left:calc(50% - 160px);width:320px;--bg-overlay-color:rgb(var(--color_32))}. The background layer inside paints with .LNYVZi{background-color:var(--bg-overlay-color)}. Class rules on the panel already reach the dialog (the projector binds both elements), but these id rules reach only the hidden copy. The dialog had no offset and no paint variable.
  2. The dialog keeps the user agent box. The source panel is a div, so it never had the user agent's dialog box: 1em padding, a 3px solid border, Canvas (white) paint, auto margins, insets and size caps. On the dialog these pushed the content 13px in and drew a white, black-framed box. The block stylesheet already resets that box for in-place dropdowns, but not for modals.

What changed

  • CapturedDialogProjector: when another element still holds the captured root's id, the dialog carries it as its editor anchor class (blocks-engine-editor-anchor-MENU_AS_CONTAINER). The capture's own panel envelope (data-blocks-engine-capture-dialog-panel) does not count, because its id is the wiring key.
  • ScopedAnchorSelectorProjection::identities(): an id that a source element carries as its editor anchor class is projected as :is(#id,.blocks-engine-editor-anchor-id). This is the mechanism already used to keep id selectors live across document variant id suffixes. The specificity shims are unchanged, so the rule keeps its source weight.
  • CapturedDialogConverter: a modal whose own author rules position it (position:fixed or absolute) gets placement: "source". The cascade is resolved through the compilation's StyleResolver.
  • CapturedDialogBlockGenerator: :where(dialog:not([data-blocks-engine-presentation])[data-blocks-engine-placement="source"]) resets inset, size caps, margin, padding, border, background and color. It has zero specificity, so every source rule still wins. Modals that do not position themselves keep the user agent box, which is their only placement.

These are two causes in one PR because neither alone puts the column where the source has it: the offset needs both the id rule (left, width) and the box reset.

Tests

  • New tests/unit/captured-dialog-source-panel-box.php, 5 assertions (projector, compiled CSS, block placement, stylesheet). On trunk, 4 fail; all pass here.
  • capture-evidence-equivalence: the 12 native dialog cases are re-recorded and noted in its README. Only the emitted style.css text and the hashes derived from it change (7a7af898 also gains one style-rule cache hit). Checked by replaying each case on trunk and on this branch and diffing the normalized output.

End to end

Studio imports of the Dunbar Oaks capture with v0.38.1 (the last base that reads these captures), iPhone user agent at 375. Quality gate passed on both.

🤖 Generated with Claude Code

…dialog

A captured phone menu panel is held twice: the capture's hidden wired copy
keeps the panel's id, and the captured state becomes the native dialog under
a generated id. On a Wix phone menu that lost two things:

- Author rules that name the panel by id (its left offset, width and the
  --bg-overlay-color its background layer paints with) reached only the
  hidden copy. The dialog now carries the held id as its editor anchor
  class, and the author stylesheet projector keeps those id selectors live
  through that class, as it already does for document variant ids.
- The dialog kept the user agent's box geometry (1em padding, a border, auto
  margins, insets and size caps), which the source div never had. A modal
  whose source panel positions itself (position fixed or absolute from its
  own rules) is now marked placement "source", and the block stylesheet
  resets that geometry under zero-specificity rules so the source rules place
  it. Canvas paint stays as the backdrop for the panel's own layers.

Refs #2726

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ellenbauer
ellenbauer force-pushed the fix/projected-dialog-source-box branch from 94083dc to 85ee265 Compare October 13, 2026 16:42
@chubes4
chubes4 marked this pull request as ready for review October 13, 2026 22:45
# Conflicts:
#	php-transformer/tools/capture-evidence-equivalence/README.md
# Conflicts:
#	php-transformer/tools/capture-evidence-equivalence/README.md
@chubes4
chubes4 merged commit ed3bc13 into trunk Oct 13, 2026
11 checks passed
@chubes4
chubes4 deleted the fix/projected-dialog-source-box branch October 13, 2026 23:31
ellenbauer added a commit that referenced this pull request Oct 14, 2026
…#2783)

* fix(php-transformer): open a projected modal in its source open state

Review of #2744 found that a projected captured modal could still open
invisible or keep the browser's dialog box:

- Id rules never matched the dialog during analysis. Its id is generated and
  the source panel id rides on its editor anchor class, so placement "source"
  missed a panel positioned only by id rules. The selector matcher and both
  candidate indexes now treat an element carrying an id's editor anchor class
  as that id's holder.
- A rest rule that hides the panel (by id or by class) made the dialog
  hidden content and the dialog block was dropped. A captured dialog's own
  rest state no longer counts as hidden; the block owns its closed state.
- Without a captured ancestor state, the rest rule won on the open dialog
  (`#panel{display:none;transform:translateX(-100%)}` against
  `body.nav-open #panel{display:block;transform:none}`). For each hiding or
  offsetting property that a state rule on the same panel reverses, the
  dialog now carries the state value inline. The block stylesheet's important
  closed-state rule still closes it.

Refs #2726

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(php-transformer): trust only agreeing or recorded state rules for a modal's open values

Review of c0b3e89:

- The last state rule in source order won: `.dark-theme #panel{opacity:.5}`
  after `body.nav-open #panel{opacity:1}` gave the dialog `opacity:.5`, and
  `.no-js #panel{display:block}` replaced `display:flex`. When the capture
  recorded an opened ancestor class, only state rules naming it are trusted;
  otherwise every state rule must agree on a property, or nothing is carried.
- A centring `translate(-50%,-50%)` counted as a rest offset, so
  `.no-js #panel{transform:none}` un-centred a modal that was fine on trunk.
  Only a translation of a full panel (100%) or viewport (100vw, 100vh) or
  more now counts as moving the panel out of view.
- A dialog none of whose triggers can ever show (no viewport shows the
  trigger or an ancestor) is dropped again instead of surviving as an
  orphaned closed dialog.

Refs #2726

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants