Skip to content

Fold a captured mobile menu dialog into the navigation it repeats - #2705

Merged
chubes4 merged 4 commits into
trunkfrom
fix/mobile-menu-dialog-into-navigation
Oct 13, 2026
Merged

chubes4 merged 4 commits into
trunkfrom
fix/mobile-menu-dialog-into-navigation

Conversation

@jp-imagines

Copy link
Copy Markdown
Contributor

This PR is stacked on #2691. Its base is that branch. Please retarget to trunk after #2691 merges. Only the last two commits belong to this PR. It was also built and checked together with #2694 (shared header part), because the header sharing it touches only matters there.

What was wrong

Some headers draw their menu twice. One copy is a row of items for wide screens. The other is a hamburger that opens a panel with the same items for narrow screens.

When the capture records that panel as a dialog, the importer turned it into two separate things:

  • the hamburger became a plain core/button
  • the panel became a custom Dialog block

The row next to them stayed a core/navigation with no hamburger of its own. On a phone, the Dialog panel held the four top-level labels as buttons that opened nothing. The links under them were not in the panel at all.

What changed

When a captured menu dialog only repeats a row whose items open dropdown panels, the row's navigation now takes it over. Each entry in the dialog must match an item the row already has: a link by label and href, or an accordion button by label. At least one entry must match a dropdown item. If anything in the dialog is not in the row, nothing changes.

When that holds:

  • The navigation gets overlayMenu: "mobile". Core draws the hamburger, the close button and the responsive container.
  • The hamburger button and the Dialog block are dropped.
  • If the hamburger sat in a wrapper that only repeated controls the row already has (a second language switcher), that wrapper is dropped too. One switcher is kept.
  • Dialog entries that the row shows outside its items (a call-to-action link) are added to the overlay as a core/buttons block. They are hidden at the wide layout. The row's own copy is hidden at the narrow layout.
  • The row is shown again below the source breakpoint (read from the source styles, 767px here). Above it, nothing moves.
  • The dropdown items open on click, as they did in the source.
  • The overlay takes the colours of the dialog panel. The header gives up its blur while the overlay is open, so the overlay covers the screen as Core draws it.

If the row cannot hand its whole menu to one navigation (for example its items are split by a control), the dialog is kept as it was captured.

The same change had a side effect. A dialog next to the header used to stop the shell search one level too deep, so it found the same header on every page. With the dialog gone, pages that start with empty decoration blocks found a different, smaller block as their header. Those pages stopped sharing the header part (6 of 14 on the test site). The search now skips empty decoration groups ahead of the header.

The changes are in NavigationPattern::menuRowBesideControls (the fold), a new CapturedMenuDialogFold (what counts as a copy), and ShellExtraction::collectNestedChrome (the empty groups).

How I tested it

New checks:

  • tests/unit/navigation-menu-dialog-fold.php uses made-up markup (two dropdown items, a link, a switcher, a call to action, a hamburger and a captured dialog). It fails on Keep a menu row as navigation when it also holds a language switcher #2691 (7 of 19 checks) and passes with the change. It also checks three cases that must not fold: a dialog with an entry the row lacks, a row split by a control, and a hamburger wrapper with its own content.
  • tests/contract/shared-menu-with-folded-dialog.php compiles three made-up pages, two with empty decoration blocks ahead of the header. It expects one header part, one navigation entity, and no copy of the menu in page content. It fails without the ShellExtraction change.

Both are added to test:canonical. The full composer test:canonical passes (no PHP memory limit). phpcs and phpstan are clean.

I also imported a real capture (a 14-page orthodontics site made with Data Liberation Agent) with a paired Static Site Importer package, with #2691, #2692 to #2697 and #2694 merged in. Home page:

Before After
Hamburger core/button in the header part 1 0
Custom Dialog blocks in page content 14 (one per page) 0
core/navigation blocks in the header part 1 (no hamburger) 1 (overlayMenu: mobile)
Links in the open phone menu (390px) 2 (Contact, Book Consultation) 22 (4 submenus with 4, 11, 3 and 2 links, Contact, Book Consultation)
Submenu labels in the phone menu that open anything 0 of 4 4 of 4 (Core lists them open)
Hamburger visible at 599, 700, 767px yes (button) yes (Core button)
Inline menu at 768 and 900px yes yes
Item positions at 1440px (Home, Treatments, Journey, Airway, Contact) 451, 539, 666, 893, 988 451, 539, 666, 893, 988
Language switcher and call to action at 1440px x 1076, 1110, 1159 x 1076, 1110, 1159
Menu item font at 1440px 14px, 500 14px, 500
Pages bound to the shared header part 14 of 14 14 of 14
wp_navigation entities 1 1
Invalid blocks (header part, navigation entity, home page) 0 0

Browser checks at 390px: a real mouse click and a touch tap both open the overlay. The close button, Escape, and following a link (a top-level link and a child link) all work. The page does not scroll sideways at any width tried. In the block editor the header part has one core/navigation, no hamburger button and no Dialog block. The navigation entity holds 4 submenus, 21 links and the call-to-action buttons block.

The paired package was built with a small stand-in for homeboy (it runs npm run build and zips the runtime files). It has no lint or review step.

What is not fixed

  • The source phone menu is an accordion: the four labels start closed and one opens at a time. Core's overlay lists every submenu open and has no way to collapse them without custom script. All 22 links are reachable without extra taps. Matching the accordion would need a runtime script, which I left out.
  • The source overlay was a panel under the header bar. Core's overlay covers the whole screen. The colours come from the source panel; its blur does not.
  • Top-level items with a dropdown are about 4px narrower than the source because Core draws its own arrow (already noted in Keep a menu row as navigation when it also holds a language switcher #2691). Home sits at x 451 where the source has 434.
  • If a hamburger and its dialog are converted before the row (a source that puts the hamburger first), and the row then declines the fold, the dialog has already been dropped. I did not find such a source.
  • No Data Liberation Agent change was needed. I opened the source accordions at 390px: each lists the same links as the desktop dropdown plus Contact and the call to action, and the capture already has those from the desktop dropdowns.

🤖 Generated with Claude Code

xxxxxxxxxxxxx and others added 3 commits October 9, 2026 12:15
…tion beside non-menu controls

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…igation it repeats

A header can draw its menu twice: a row of dropdown items for wide screens
and a hamburger that opens a panel with the same items for narrow screens.
When the capture records that panel as a dialog, the importer kept a plain
button for the hamburger and a custom Dialog block for the panel, beside a
core/navigation that had no hamburger of its own.

When every entry in the dialog is an item the row already has, the row's
navigation now takes it over: overlayMenu is mobile, the hamburger and the
dialog are dropped, a hamburger wrapper that only repeats row controls is
dropped, and dialog entries the row shows outside its items go into the
overlay. The row is shown again below the source breakpoint with the
repeated controls hidden.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…come first

The shell search takes the first block of a level as the header only when it
holds the navigation. Pages that start with empty decoration groups skipped
that check and settled on the navigation block alone, so they stopped sharing
the header part with pages that did not. Skip empty decoration groups ahead of
the header.

This showed up once a folded menu dialog no longer sat beside the header's
contents and kept the search one level deeper on every page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Base automatically changed from fix/header-nav-button-dropdown to trunk October 13, 2026 22:44
# Conflicts:
#	php-transformer/composer.json
#	php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php
#	php-transformer/src/HtmlToBlocks/Support/NavigationToggleSuppressor.php
#	php-transformer/src/WordPressSitePlan/ShellExtraction.php
#	php-transformer/tests/unit/navigation-menu-row-beside-controls.php
@chubes4
chubes4 marked this pull request as ready for review October 13, 2026 23:48
@chubes4

chubes4 commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Retargeted to trunk after #2691 merged, then merged current trunk:

  • Kept trunk's footer search, which steps over a trailing projected modal dialog, and combined it with this PR's skip over empty leading groups.
  • The hamburger check in projectedOverlayMenu() now names the trigger through CaptureEvidence::DIALOG_TRIGGER.
  • The two new test fixtures moved to the neutral capture vocabulary from refactor: neutral capture vocabulary for Blocks Engine capture evidence #2725, using the published rename rule.

The PR's own tests, the shared-shell contracts, the vocabulary contract and capture-evidence equivalence all pass locally. Equivalence needed no re-recording.

Merge and test by Claude (Opus) via OpenCode on behalf of @chubes4.

@chubes4
chubes4 merged commit da0e113 into trunk Oct 13, 2026
11 checks passed
@chubes4
chubes4 deleted the fix/mobile-menu-dialog-into-navigation branch October 13, 2026 23:59
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.

3 participants