Skip to content

feat(webkit): add the Menu compound and move MenuItem into it - #853

Merged
isaque-bock-azion merged 9 commits into
mainfrom
feat/menu-component
Aug 5, 2026
Merged

feat(webkit): add the Menu compound and move MenuItem into it#853
isaque-bock-azion merged 9 commits into
mainfrom
feat/menu-component

Conversation

@gabriel-lisboa-azion

Copy link
Copy Markdown
Collaborator

A vertical, hierarchical navigation menu. It owns no shell and no layout of its own — a host (usually Sidebar, but any scroll container works) supplies the surface, the scroll region and the width — and renders three structures through one compound:

  • groups that separate rows under a title,
  • condensed rows that own children and expand in place behind an indent rail,
  • drill rows that replace the menu with a second-level menu and return.

Unlike navigation-menu (the horizontal megamenu) this menu is vertical, recursive, and owns a view stack. Its box model follows Cloudflare Kumo's Sidebar; per migration.md only the structure is adopted, with every value expressed in our own tokens — the mapping is in the spec's Tokens section.

MenuItem moves into the compound

menu-item.vue moves from navigation/menu-item/ to navigation/menu/menu-item/, so it is a real sub-component rather than a sibling reached across folders. This is additive for consumers: the ./menu-item export path still resolves, and its props, events, slots and every default are unchanged. In the catalog it becomes kind: subcomponent, parent: menu, exactly like menu-group and command-menu-item.

Two drifts against the rows it sits beside are fixed:

  1. An empty icon box no longer renders. A size-8 box with no glyph in it reserved a column the row did not use, so an icon-less row's label sat off the column its siblings hold and a nesting rail anchored to blank space.
  2. The anchor and button branches now share one label class. They were .text-label-sm (12px) and .text-body-sm (14px), so the same menu rendered two sizes depending on whether a row happened to carry an href — children came out smaller than their parents. Both are .text-label-md.

Specs

.specs/menu-item.md is removed and its Props / Events / Slots fold into .specs/menu.md's Sub-components entry for Menu.Item — a sub-component of a compound is documented in the compound's spec, the way menu-sub and menu-group already are. MenuItem.stories.js is likewise removed; the rows are documented through Menu.stories.js, and menu-item.test.ts renders the component directly instead of composing a story fixture (which testing.md allows).

sidebar-group.test.ts here carries only the moved import path. Its story-fixture rewrite belongs to the stacked Sidebar PR, which is what makes the Sidebar stories compose Menu.

Deliberately out of scope

The wider row redesign (a size-3 icon, var(--primary) on the selected icon, ::before/::after ghost layers replacing the root transition-colors) is a visual breaking change and lands later as a spec_version: 2 revision on a major. This PR renders today's row: correct behaviour, old visuals.

Follow-up in this PR

Linux visual baselines still to be regenerated via CI (the new Menu stories add baselines, and the label-class change moves the existing Sidebar-composed ones).

A vertical, hierarchical navigation menu: groups that separate rows under a
title, condensed rows that expand in place behind an indent rail, and drill
rows that replace the menu with a second-level menu. It owns no shell — a
host (usually Sidebar) supplies the surface, the scroll region and the width.

MenuItem moves into the compound's folder so it is a real sub-component
rather than a sibling reached across folders. Its ./menu-item export path,
props, events, slots and every default are unchanged, so this is additive
for consumers. Two drifts against the rows around it are fixed: an empty
icon box no longer renders (it reserved a column the row did not use,
pushing an icon-less label off the column its siblings hold), and the anchor
and button branches now share one label class, so a menu no longer renders
two text sizes depending on whether a row happens to carry an href.

Its spec folds into .specs/menu.md — a sub-component of a compound is
documented in the compound's spec, the way menu-sub and menu-group already
are — and .specs/menu-item.md is removed.
Comment thread packages/webkit/src/components/navigation/menu/menu.test.ts Fixed
Comment thread packages/webkit/src/components/navigation/menu/menu.test.ts Fixed
Comment thread packages/webkit/src/components/navigation/menu/menu.test.ts Fixed
Comment thread packages/webkit/src/components/navigation/menu/menu.test.ts Fixed
Comment thread packages/webkit/src/components/navigation/menu/menu.test.ts Fixed
Comment thread packages/webkit/src/components/navigation/menu/menu.test.ts Fixed
… rows

30 added — Menu's five stories (Default, Types, Condensed, Drill, Disabled)
across the six modes.

8 modified — the Sidebar stories compose MenuItem, so they pick up the label
class moving to .text-label-md and the icon box no longer rendering when
empty. Only the mobile and tablet renders move: on the narrower canvases the
same shift crosses the 1% pixel threshold that desktop absorbs.
@gabriel-lisboa-azion
gabriel-lisboa-azion marked this pull request as ready for review August 5, 2026 00:58
@gabriel-lisboa-azion
gabriel-lisboa-azion requested a review from a team as a code owner August 5, 2026 00:58
@CLAassistant

CLAassistant commented Aug 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@isaque-bock-azion isaque-bock-azion added the WIP Work in Progress label Aug 5, 2026
gabriel-lisboa-azion and others added 4 commits August 5, 2026 12:16
The inline submenu indent was spacing-sm + spacing-md, which pushed nested
rows far enough right that a two-level tree read as two unrelated lists. Step
the second term down to spacing-xs so the rail and the nested label stay in
one column.
… root

CodeQL flagged all six `Menu.<Part>` reads in the compound-API test as property
access on an undefined base: its extractor cannot resolve the default export of
a `.vue` module, so the compound root is statically undefined and each member
access looks like a null dereference.

Assert the root once with `expect(Menu).toBeDefined()` and read the members
through `?.`. The contract is unchanged — a member that is not attached still
fails on `undefined !== expected` — and the guard turns a would-be runtime
TypeError into a direct "Menu is undefined" failure.
…dent

The 12 refreshed snapshots are Menu's Types and Condensed stories across all six
modes — the only two that render inline sub-content, so the only two the narrower
--menu-indent moves. Every other baseline is byte-identical.
@isaque-bock-azion isaque-bock-azion removed the WIP Work in Progress label Aug 5, 2026
@isaque-bock-azion
isaque-bock-azion merged commit 9a0adae into main Aug 5, 2026
23 checks passed
@isaque-bock-azion
isaque-bock-azion deleted the feat/menu-component branch August 5, 2026 18:17
isaque-bock-azion added a commit that referenced this pull request Aug 5, 2026
Brings the Menu compound (#853); menu-item resolved keeping main's
semantics in canonical token syntax, the retired menu-item spec deleted,
the compound's new files canonicalized and the catalog regenerated.
gabriel-lisboa-azion added a commit that referenced this pull request Aug 5, 2026
The Menu landed on main in #853 as a squash, so every menu file came back as an
add/add conflict against this branch's copy. Main's side wins throughout: it is
strictly newer, carrying both the tightened inline sub-content indent
(spacing-sm + spacing-xs) and the guarded compound assertions.

The sidebar baselines this branch deleted stay deleted: it folded the header
search and profile footer stories into the richer Default and Resizable ones,
so their snapshots are obsolete. The remaining baseline conflicts take main's
copy and are regenerated in CI on top of this merge.
gabriel-lisboa-azion added a commit that referenced this pull request Aug 10, 2026
Bring 254 commits of main's webkit into the sample branch, keeping the
illustration system that lives only here.

Resolution:
- Visual baselines (51 PNGs): main's — CI-generated, main is authoritative.
- Sidebar / Menu / Switch cluster: main's. The same two commits exist on
  both sides (unsquashed locally, squash-merged as #853/#854 on main);
  main carries the reviewed final state.
- Illustration tokens (animate.js, keyframes.js, .size-limit.json,
  DESIGN.md): kept — main's side held no competing change.
- code-block.md / webkit-ui-craft SKILL.md: kept HEAD's frontmatter; both
  bodies are supersets of main's.
- Generated artifacts (theme dist v4 globals.css/.scss, catalog.json):
  regenerated from the merged sources rather than hand-merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants