feat(menu, context-menu): standardize the API reference#241
Merged
Conversation
Rename the non-standard prop names across both menu families and make the generated docs table trustworthy enough to publish. Naming, following the button-family rework (ed3b525): - Rows in both families rename `tone` to `variant` — one look axis, same value spellings, now defaulting to `neutral` so the prop is optional. ContextMenu's was required where Menu's was not; they now agree. - `MenuItemTone` / `ContextMenuItemTone` / `ContextMenuSubmenuTriggerTone` become `*Variant`. - The rows' `endContent` prop is renamed `trailing`, and the styled part `MenuItemEndContent` is renamed `MenuItemTrailing`. - The elements menu row `layout` axis renames its values from `default` / `with-description` to `single` / `stacked`. Fixes `MenuItem` dropping a consumer-supplied `render`: the ready-made hard-coded its own render target after spreading props, so `<MenuItem render={<a href=... />} />` silently rendered a `<div>` — which is what the 11 collapsed-crumb rows in the Breadcrumb stories were doing. The prop is now threaded into the styled row and typed to what it actually accepts, with a regression assertion on the tag name. Every other ready-made that hard-codes its render target now omits `render` rather than advertise a prop it ignores. Docs-table corrections: - `MenuContent`'s `sizing` claimed an `"anchor"` default it never applied. - `MenuSubmenuContent` inherited `MenuContent`'s `side` default of `"bottom"` when it actually opens to the `"right"`; the shared props type is split so each surface declares its own. - `ContextMenuContent`'s positioning props are documented per position rather than with a single default, since the same component anchors to the pointer at the root and beside its parent row in a submenu. - Every Menu and ContextMenu prop now carries a description and, where a real default exists, a `@default` tag. Shared docs infrastructure: - `PropsTable.astro` adopts the Prop / Type / Default / Description shape, with required marked by `*` (plus sr-only text), `—` for absent defaults, the component description above each table, a real empty state, and a visible error box when a part cannot be resolved. - Fixes a docgen filter that dropped propel-declared props shadowing a native attribute (docgen points `parent` at @types/react). Testing `declarations` instead recovers 23 props across 16 components, including IconButton's required `aria-label`. Kept in sync with .storybook/main.ts as its comment claims. Menu's docs page grows from 3 documented parts to all 17; ContextMenu's from 2 to all 12. AGENTS.md: rewrite the prop-vocabulary rules, which still mandated `prominence` / `tone` / `magnitude` that ed3b525 had already replaced, and fix rule 12's claim that no cva defines `defaultVariants` (table, icon and shortcut already did). Claude-Session: https://claude.ai/code/session_01G2g3qvyp1r5nDydrHk955N
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
propel-docs | 060f17e | Jul 25 2026, 07:48 PM |
These demos live outside `demos/menu/`, so they were missed by the Menu pass and only surfaced in `astro check` — the one gate that could not run locally, because tsdown and the oxlint plugin both need Node 24 (`.node-version`) and fall back to an uninstalled optional peer on Node 22. Claude-Session: https://claude.ai/code/session_01G2g3qvyp1r5nDydrHk955N
|
📚 Storybook preview: https://pr-241-propel-storybook.vamsi-906.workers.dev |
vihar
approved these changes
Jul 26, 2026
9 tasks
sriramveeraghanta
added a commit
that referenced
this pull request
Jul 26, 2026
Extend the button-family rework (#239) and the menu/context-menu API standardization (#241) across every component, adopting Kumo's prop vocabulary and its component-page format. Props: rename magnitude to size library-wide (sm/md/lg/xl spellings unchanged) and fold tone into variant with each union kept intact, so Badge carries variant="danger" and variant="crimson" on one axis exactly as Kumo's Badge does. Also emphasis to variant (Pill), appearance to variant (Tabs), presentation to appearance (NavigationMenuLink), and mode to variant (Table, whose root cva is corrected to tableVariants per rule 8). density stays: CheckboxGroupField and RadioGroupField already spend size on the label text scale, so option spacing needs its own axis and Kumo has no density vocabulary to borrow. sizing, fillType, surface, elevation, visibility, placement and layout stay for the same reason. size collides with the native <input size> attribute, so Input, AutocompleteInput, NumberFieldInput, OTPFieldInput and TextArea omit it from their props types -- the same fix Kumo applies. Docs: reorder all 62 component pages to Kumo's structure (lead demo, Installation, Usage, Examples with cases as H3, API Reference) inside a new ComponentSection wrapper, and switch PropsTable to Kumo's Prop/Type/Default/Description columns with required marked on the prop name. propel ships no barrel export, so Installation keeps a single granular block rather than Kumo's Barrel/Granular split. Rewrite the prop-vocabulary rules in packages/propel/AGENTS.md and docs/design.md, which mandated the opposite convention, and correct six JSDoc examples still documenting the prominence prop dropped in #239. Claude-Session: https://claude.ai/code/session_01G2g3qvyp1r5nDydrHk955N
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First two components of the API-reference standardization pass, plus the shared docs infrastructure the remaining 62 components will inherit.
Breaking API changes
tonevariantendContenttrailingMenuItemEndContentMenuItemTrailingelements/menuMenuItemToneMenuItemVariantelements/menuContextMenuItemToneContextMenuItemVariantelements/context-menuContextMenuSubmenuTriggerToneContextMenuSubmenuTriggerVariantelements/context-menulayout="default" | "with-description"layout="single" | "stacked"elements/menurowsvariantnow defaults toneutralin both families, so it is optional. ContextMenu'stonewas required where Menu's was not — the two families disagreed on the same concept.Value sets stay as they were: Menu rows are
neutral · accent · danger, ContextMenu rows areneutral · danger. Addingaccentto ContextMenu is a design call, not a naming one, so I left it — happy to follow up if you want them identical.Bug fix:
MenuItemsilently droppedrenderThe ready-made hard-codes its render target after spreading props:
so a consumer's
renderwas always overwritten.breadcrumb.stories.tsxpassesrender={<a href="#" />}to 11 collapsed-crumb rows — every one was rendering a<div role="menuitem">instead of a real anchor.renderis now threaded into the styled row (the correct graft target per rule 1a) and typed to what it actually accepts. Locked in with a regression assertion, which I confirmed fails without the fix:Eleven other ready-mades had the same overwrite but no callers; those now omit
renderrather than advertise a prop they ignore.Wrong values the props table was publishing
MenuContent.sizingadvertised@default "anchor"— nothing applied it; omitting it hugs the content.MenuSubmenuContent.sideinherited"bottom"fromMenuContentbut actually opens"right". The shared props type is split so each surface declares its own.ContextMenuContent's positioning props deliberately carry no@default: the same component is the root surface (pointer-anchored) and every submenu surface (inline-end), so one default would be a lie on the other. Also documented that an explicitsideopts out of Base UI's pointer-hugging offsets.Shared docs infrastructure
PropsTable.astroadopts the Prop · Type · Default · Description shape — required marked*(with sr-only text),—for absent defaults,ref/keyfiltered, the component's description above its table, a real empty state, and a visible error box when a part can't be resolved.Fixed a docgen filter bug: props propel declares that shadow a native attribute were silently dropped, because docgen sets
parentto the@types/reactdeclaration. Testingdeclarationsinstead recovers 23 props across 16 components — includingButton.disabled/type,TextArea.value, andIconButton's requiredaria-label. Kept in sync with.storybook/main.ts, as that file's comment already claimed.Menu's page grows from 3 documented parts to all 17; ContextMenu's from 2 to all 12. Both use
## API Reference.AGENTS.md
Rewrote the prop-vocabulary rules, which still mandated
prominence/tone/magnitudethat ed3b525 had already replaced — the protocol was actively contradicting the code. Also fixed rule 12's claim that no cva definesdefaultVariants(table,iconandshortcutalready did).Verification
check:types✅ ·check:format✅ (propel + docs)Two caveats, both reproducing on a clean tree and not introduced here:
pnpm check:lintfails outright —.node-versionpins 24.16.0, the running Node is 22.17.1, which can't load the.tsoxlint plugin. I linted viaNODE_OPTIONS=--experimental-strip-types.pnpm buildfails —tsdown@0.22.9can't resolveunrun, which isn't in the install tree. This blocksdocs:check:types, so the docs app's typecheck is the one gate I could not run; I validated the table against the real docgen parser directly instead (all 29 parts resolve, zero props missing a description).Worth clearing both before the next component — #2 means docs typechecking is currently unavailable to everyone.
https://claude.ai/code/session_01G2g3qvyp1r5nDydrHk955N