Skip to content

Restyle web menus to match the menu design - #12310

Open
mozzius wants to merge 11 commits into
samuel/app-3734-more-inline-menusfrom
samuel/app-3734-web-menu-design
Open

mozzius wants to merge 11 commits into
samuel/app-3734-more-inline-menusfrom
samuel/app-3734-web-menu-design

Conversation

@mozzius

@mozzius mozzius commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Part of APP-3734. Stacked on #12296.

Restyles the web dropdown menu (src/components/Menu/index.web.tsx) to match the design system's menu design (Figma "Single select menu"), so it looks like the Android inline menus from #12271. No call sites change.

Part Now
Container 16px radius, 4px padding, 0.5px border_contrast_low border, the Figma "Large Drop" shadow (0 16px 24px, 8% black in light, 40% in dark and dim), 160px min width
Items 40px rows, 13px medium labels in the default text colour. Destructive is negative_500, disabled text_contrast_low
Icons 18px, a little under md, to balance the 13px labels. Items have 4px less padding on the leading side (12px), so icons sit closer to the edge. Always on the leading side, in the text colour, whatever order they're written in. Item re-arranges its own children, like the native inline code. ItemIcon's position now does nothing on web
Radio items The check's slot is always reserved, so labels don't move. Selected: a primary_50 pill, a primary_500 label, and the mid-weight Check_Stroke3 from #12271. Dark and dim go a shade lighter (primary_100 and primary_600; their palettes are inverted), so the selected state isn't muddy. A radio item's icon trails, and turns primary when selected
Section titles (LabelText) 13px semibold in the default text colour, not grey
Dividers 4px of space above and below, so a hovered item's fill doesn't touch them
Hover and focus bg_contrast_50 in light, bg_contrast_100 in dark and dim. A hovered selected item deepens to primary_100
Keyboard focus A 2px primary_500 ring inside the item, on top of the hover fill. The fill alone was about 1.1:1 on white. Radix also focuses items as the pointer moves, so the ring only shows when the browser treats the focus as keyboard focus (:focus-visible). Arrow keys move between items; Radix keeps Tab inside the menu, as usual for menus

Shapes

These follow Material 3's segmented menu tokens, like Android:

  • 4px corners between items.
  • 12px outer corners on the menu's first and last items: the container's 16px radius minus its 4px padding, so they're concentric.
  • 12px bottom corners on an item directly above a label. In practice that's the share menu's "only visible to logged-in users" footnote.
  • The selected pill is 8px all round, whatever its position. Hovered items keep their positional corners.
  • 2px between neighbouring items (SegmentedGap), so a hovered item's fill doesn't touch the selected one.

On web, Group doesn't wrap its items, so every item in a menu is a DOM sibling. That includes items rendered by custom components, like PostMenuControlItems in the post … menu. The rules in src/style.css match [data-menu-item] with :nth-child(1 of …), :nth-last-child(1 of …) and :has(+ [data-menu-label]). So they count across sections and skip hidden items, with no effects or context.

The native InlineOuter counting can't do this, because it can't see inside custom components. Items carry dataSet because react-native-web's Pressable drops className. For the same reason, the old .radix-dropdown-item:focus rule has never applied; it's left as is.

Item now passes disabled to Radix, so keyboard navigation skips disabled items instead of landing on them.

Notes

  • position="right" on Menu.ItemIcon is used at 54+ call sites and now has no effect on web. Removing it could be a follow-up.
  • t.atoms.text is #000000 in light, while Figma's Text/default is #111822 (contrast_975). Android uses t.atoms.text too, so the design library and the code disagree.
  • With the lighter dark-theme shades, the selected label on its pill is about 4.5:1 in dark and 4.2:1 in dim (it was about 3.5:1). Android uses the same shades.
  • ShareMenuItems.web.tsx uses LabelText as a footnote, so its note is now semibold in the text colour rather than grey.

Testing

  • pnpm typecheck, pnpm lint, pnpm test src/components/Menu
  • Headless Chrome against Metro: compared side by side with the Figma, and the real thread and post menus logged out, in light, dark and dim. Measured the computed corners and gaps for hovered, selected and end items
  • Keyboard, in headless Chrome: Enter on the trigger opens the menu on the first item, and arrow keys show the ring in light and dark. Mouse-only hover shows no ring
  • Logged in, recorded below
  • Safari and Firefox. The shapes need :nth-child(… of …) and :has() (Safari 15.4+, Firefox 121+); otherwise items just keep 4px corners

Video

web-menu.mov

🤖 Generated with Claude Code

@mozzius
mozzius added this pull request to stack #12297 November 2, 2026 18:27
@linear-code

linear-code Bot commented Nov 2, 2026

Copy link
Copy Markdown

APP-3734

@mozzius mozzius changed the title samuel/app 3734 web menu design Restyle web menus to match the menu design Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

The Pull Request introduced native fingerprint changes against the base commit.


Generated by PR labeler 🤖

@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor
Size Old size New size Diff
Uncompressed 9.78 MB 9.78 MB +3.00 KB (0.03%)
Gzip 2.86 MB 2.86 MB +1009 B (0.03%)

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from 0ab3878 to 425ce1c Compare November 3, 2026 07:56
@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from 4039efe to c9e2950 Compare November 3, 2026 08:04
@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from c9e2950 to cb21945 Compare November 3, 2026 10:01
@mozzius
mozzius marked this pull request as ready for review November 3, 2026 14:04
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

I found no actionable defects in this PR.

Claude review, run

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from cb21945 to da2295d Compare November 5, 2026 09:06
}
return {
selected: radio?.props.selected,
content: radio ? [radio, ...rest, ...icons] : [...icons, ...rest],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: a disclosure chevron ends up in front of the label

Moving every ItemIcon to the leading side also moves directional affordances that only make sense trailing. On web, the composer's language menu (src/view/com/composer/select-language/PostLanguageSelect.tsx:126-129, which has no .web variant) renders "More languages..." with <Menu.ItemIcon icon={ChevronRightIcon} /> after the text. With this change the right-pointing chevron is drawn before the label (› More languages...), so it no longer reads as "opens more". The old ItemIcon honoured the written order.

Possible fixes: keep trailing-only glyphs like ChevronRight after the text in arrangeItemContent, keep position="right" meaningful on web, or change that call site.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a3e1ade: arrangeItemContent keeps a right chevron trailing, like Android's submenu arrow, so "More languages..." reads as opening more options again.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

One non-blocking finding, posted inline at src/components/Menu/index.web.tsx:380.

  • A disclosure chevron is drawn before its label. The new arrangeItemContent moves every Menu.ItemIcon to the leading side. The composer's language menu shares one file across platforms, and on web its "More languages..." item now shows its ChevronRight before the label. That's at src/view/com/composer/select-language/PostLanguageSelect.tsx:129.

I found no other defects.

Claude review, run

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

I found no defects in this PR that need fixing.

These are the parts I checked:

  • Child reordering in Item: I checked arrangeItemContent against every Menu.ItemRadio call site, the ThemePicker swatch and the PostLanguageSelect chevron. All of them keep their content.
  • ContextMenu on web: it re-exports these same components, so it picks up the new styling.
  • Icon sizing: ItemIcon now sets width={18}, and the icon helper in src/components/icons/common.tsx supports that.
  • react-keyed-flatten-children: it's already listed in package.json.

Most CI jobs (Lint, PR Tests, Lockfile) were still running when I looked, so this review doesn't cover their results.

Claude review, run

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from a3e1ade to fe6b6a4 Compare November 5, 2026 11:42
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

I found no defects to act on in this PR.

Claude review, run

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from fe6b6a4 to ac3d812 Compare November 5, 2026 21:52
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

I found no actionable defects in this PR.

Claude review, run

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from ac3d812 to d1d9e7e Compare November 6, 2026 13:18
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

I found no actionable defects in this PR.

Claude review, run

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

No actionable defects found.

Claude review, run

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

I found no actionable defects in this PR.

Claude review, run

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from 0a3cca5 to 12e57c3 Compare November 9, 2026 16:27
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

I found no actionable defects in this PR.

Claude review, run

@mozzius
mozzius force-pushed the samuel/app-3734-web-menu-design branch from 12e57c3 to 785c79c Compare November 9, 2026 16:30
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

I found no defects that need fixing in this PR.

Claude review, run

mozzius and others added 11 commits November 10, 2026 11:16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers positional

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

Copy link
Copy Markdown
Contributor
Rating Why
Risk 🟡 Medium Item now reorders its own children, and CSS sibling selectors (:nth-child(… of …), :has()) set the item shapes.
Impact 🟡 Medium Every dropdown menu on web is affected, including the post, share and thread menus. The changes are visual and a JS deploy can revert them.

Findings: None.

Worth a close look

  • Fragile src/components/Menu/index.web.tsx:193: arrangeItemContent only finds ItemIcon and ItemRadio when they are direct children of Item (fragments are flattened). If an icon or radio is wrapped in a custom component, it stays where it's written and the selected styling doesn't apply. Future callers won't get a warning about this.
  • Needs a device src/components/Menu/index.web.tsx:118: The ring is gated on :focus-visible at the moment Radix moves focus. Browsers can keep treating programmatic focus as focus-visible after a keyboard interaction. On Chrome and Safari, open a menu with Enter, then move the mouse over the items, and check that the ring doesn't follow the pointer.
  • Needs a device src/style.css:418: The first/last item corners and the corners above a footnote depend on :nth-child(1 of …) and :has(). Check the post … menu and the share menu in Safari and Firefox.

Claude review, run

This branch was successfully deployed

1 active deployment
samuel/app-3734-web-menu-design - social-app PR #12310 — 443a9ed3 Deployed Nov 10, 2026 by arcalinea
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants