Skip to content

fix(php-transformer): keep wrapped peer menu items flat and off panel classes - #2743

Merged
chubes4 merged 1 commit into
trunkfrom
fix/nav-peer-item-wrappers
Oct 13, 2026
Merged

chubes4 merged 1 commit into
trunkfrom
fix/nav-peer-item-wrappers

Conversation

@ellenbauer

@ellenbauer ellenbauer commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Refs #2726

What was wrong

On St Francis Pet Hospital (Squarespace), the phone menu opened but its items sat off screen (translateX(330px)), and every link after "Home" was hidden.

Squarespace draws its phone menu as sliding panels ("folders") of item wrappers:

nav.header-menu-nav-list
  div.header-menu-nav-folder.header-menu-nav-folder--active   (root folder)
    div.header-menu-nav-folder-content > div.header-menu-nav-wrapper
      div.header-menu-nav-item > a   (Home)
      div.header-menu-nav-item > a   (About Us, Services, ...)
  div.header-menu-nav-folder   (the About Us folder, off canvas until opened)

Two generic readings in NavigationPattern broke it:

  1. Peer item wrappers were read as a submenu. primaryNavigationAnchor() and collectSubmenuContainers() treat an anchor-only wrapper that follows an item's own anchor as that item's submenu (<div><a>Parent</a><div><a>Child</a></div></div>, from bc29d45). They also applied it when the first anchor came from a sibling wrapper of the same shape. So every div.header-menu-nav-item after "Home" became a child of "Home", and the whole root folder became one closed core/navigation-submenu.
  2. Classes of list-level wrappers were carried onto each item. sharedReplacedWrapperClassNames() carries the classes of the wrappers core collapses between an item and its link. Without an li its walk did not stop at the item, so it also took the folder, folder content and wrapper classes, which hold every item. Each item then matched .header-menu-nav-folder{position:absolute;transform:translateX(100%)}. The --active state class that cancels it on the root folder is not shared by every item, so it was dropped.

Either one alone still leaves the items off screen, so both are in this PR, as two scoped changes.

What changed

  • An anchor-only wrapper is a submenu only when the parent's anchor is the wrapper's own direct child, which is the documented shape. A wrapper beside a peer wrapper is a peer item.
  • replacedAncestorWrapperClassNames() stops at a wrapper that also holds another labelled link, the same way it already stops at an li. The per-item wrapper (div.header-menu-nav-item) still carries its classes.

Tests

  • New tests/unit/navigation-peer-item-wrappers.php, 3 assertions: wrapped peers stay flat links, panel classes stay off the items, and the per-item wrapper class is still carried. All 3 fail on trunk and pass here.
  • navigation-anchor-cluster-submenu.php (the bc29d45 shapes) still passes.

End to end

Studio imports of the St Francis capture with v0.38.1 (the last base that reads these captures), iPhone user agent at 375, dialog opened through its bound trigger. Quality gate passed on both.

Still open (not in this PR)

  • The About Us folder's links now show in the same flat list instead of as a drill-down. In the source they sit in a second panel that a[data-folder-id="/aboutus"] opens. Mapping that panel to a submenu needs the data-folder-id to data-folder link, which is Squarespace vocabulary, so it is left for a separate change.

🤖 Generated with Claude Code

… classes

A list-less menu that wraps each item (`nav > div.panel > div.content >
div.item > a`, as Squarespace's phone menu folders do) was misread twice:

- Each item wrapper after the first was taken for the first item's
  anchor-only submenu cluster, so the panel became one submenu holding
  every other link. That reading now needs the parent's anchor to be the
  wrapper's own direct child, which is the documented
  `<div><a>Parent</a><div><a>Child</a></div></div>` shape.
- The shared wrapper class carry walked past the item into the wrappers that
  hold every item, so the panel's classes (and its off-canvas transform)
  landed on each item. The walk now stops at a wrapper that also holds
  another item's link, the same way it stops at a list item.

Refs #2726

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chubes4
chubes4 marked this pull request as ready for review October 13, 2026 22:44
@chubes4
chubes4 merged commit 596ff50 into trunk Oct 13, 2026
11 checks passed
@chubes4
chubes4 deleted the fix/nav-peer-item-wrappers branch October 13, 2026 22:45
ellenbauer added a commit that referenced this pull request Oct 14, 2026
…e it

Review of #2743 found two submenu shapes that fell through once peer
wrappers stopped reading as submenus:

- A Squarespace desktop folder titled by a link (`div.item > a.title +
  div.panel > div.entry > a`). The anchor-only cluster reading accepted only
  bare links, so the panel's wrapped entries came out as top-level links in
  the bar and the title lost the item's classes.
- A parent link in a label wrapper (`div > span > a + div > a`). The guard
  wanted the link as the item's direct child, so the menu was dropped.

ownsSubmenuCluster() now accepts the item's own link as a direct child or as
the only content of a label wrapper of another tag, and a cluster of links
that are bare or one per wrapper (isWrappedLinkPanel(), shared with the
aria-controls panel reading). A wrapper of the same tag beside the label is
still a peer item.

The aria-controls submenu label also leaves out screen-reader-only text.

Refs #2726

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ellenbauer added a commit that referenced this pull request Oct 14, 2026
…l as a submenu (#2781)

* fix(php-transformer): read a button that controls a sibling link panel as a submenu

A menu item made of a labelled button and the sibling panel it names with
aria-controls (`div > button[aria-controls=p] + div#p > div > a ...`) had no
reading. Squarespace's desktop folders use it. Before #2743 the first child
link was taken as the submenu label; after it the child links were
flattened into the top row; with generic names the folder was dropped.

buttonDropdownItem() now also accepts that shape when every panel entry is a
link, bare or in its own wrapper. The button label becomes the submenu label
and the panel's links its children.

Refs #2726

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

* fix(php-transformer): let an item's own link own the link panel beside it

Review of #2743 found two submenu shapes that fell through once peer
wrappers stopped reading as submenus:

- A Squarespace desktop folder titled by a link (`div.item > a.title +
  div.panel > div.entry > a`). The anchor-only cluster reading accepted only
  bare links, so the panel's wrapped entries came out as top-level links in
  the bar and the title lost the item's classes.
- A parent link in a label wrapper (`div > span > a + div > a`). The guard
  wanted the link as the item's direct child, so the menu was dropped.

ownsSubmenuCluster() now accepts the item's own link as a direct child or as
the only content of a label wrapper of another tag, and a cluster of links
that are bare or one per wrapper (isWrappedLinkPanel(), shared with the
aria-controls panel reading). A wrapper of the same tag beside the label is
still a peer item.

The aria-controls submenu label also leaves out screen-reader-only text.

Refs #2726

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

* fix(php-transformer): keep peer and titled-column wrappers out of the label submenu reading

Review of 0fc24a5: a wrapped label of another tag also matched peers and
footer columns:

- `div.list > span.item > a` beside `div.item > a` items became a submenu
  of Home holding the rest;
- `p.item > a` beside `div.item > a,a` did the same;
- footer columns `div.col > h4 > a + div.links > a,a` became closed
  submenus.

A wrapped label now owns the cluster only when the item holds exactly the
two of them, the label is not a heading, and they share neither a tag nor a
class. The isAnchorOnlyCluster() docblock is back on its method.

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