ActionMenu component - #13
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new ActionMenu molecule (trigger + anchored popover) and ActionMenuItemCore (menu row rendered as button/internal NuxtLink/external link), along with Storybook stories, consumer styling docs, and a comprehensive Vitest suite. The PR also updates Nuxt/tooling dependencies and config to support the new component stack (Popover API + CSS anchor positioning) in dev/test environments.
Changes:
- Introduce
ActionMenu.vueandActionMenuItemCore.vuewith ARIA menu semantics, Popover API integration, and keyboard navigation. - Add Storybook stories, consumer token documentation, and new unit tests + snapshots for both components.
- Update Nuxt/tooling dependencies and configuration (including
vite.optimizeDepsand Nuxt version bumps).
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Bumps Nuxt/tooling dependencies required by the new components and updated dev environment. |
| nuxt.config.ts | Updates optimizeDeps includes and Nuxt compatibility date for the runtime/dev setup. |
| modules/colour-scheme.ts | Adjusts runtimeConfig access typing for the colour-scheme module. |
| app/components/05.forms/input-button/InputButtonCore.vue | Switches NuxtLink resolution approach to align with new link/button polymorphism usage. |
| app/components/02.molecules/action-menu/ActionMenu.vue | New popover-backed action menu container with dynamic item slots and keyboard handling. |
| app/components/02.molecules/action-menu/ActionMenuItemCore.vue | New polymorphic menu item core (button/internal link/external link) with tokenized styling. |
| app/components/02.molecules/action-menu/CONSUMER-STYLING.md | Documents the public CSS token API and recommended override patterns for consumers. |
| app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts | Adds Storybook coverage for common usage variants and in-context examples. |
| app/components/02.molecules/action-menu/tests/ActionMenu.spec.ts | Adds ActionMenu unit tests covering structure, ARIA, focus behavior, and keyboard nav. |
| app/components/02.molecules/action-menu/tests/ActionMenuItemCore.spec.ts | Adds ActionMenuItemCore unit tests covering rendering modes, slots, and emits. |
| app/components/02.molecules/action-menu/tests/snapshots/ActionMenu.spec.ts.snap | Adds snapshots for ActionMenu test suite. |
| app/components/02.molecules/action-menu/tests/snapshots/ActionMenuItemCore.spec.ts.snap | Adds snapshots for ActionMenuItemCore test suite. |
| .claude/skills/index.md | Registers the new ActionMenu skill doc in the skills index. |
| .claude/skills/components/action-menu.md | Adds component skill documentation for AI assistance/reference. |
| .claude/settings.json | Updates Claude permissions/config (currently includes non-portable absolute paths/commands). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
srcdev
added a commit
that referenced
this pull request
Aug 6, 2026
…rfaces - --theme-text-inverted's light-mode branch matched --theme-surface-inverted's own light-mode branch, making InputButtonCore's .secondary text invisible in light mode. Switched to --colour-theme-0. - InputButtonCore's base and .tertiary hover states paired --theme-surface-hover (dark in light mode) with --theme-text (also dark in light mode) instead of --theme-on-surface — same invisible-text bug. Documented as pitfall #13 in Claude.md. - 00.element-defaults.css referenced --theme-button-surface, a token never declared anywhere in the codebase (typo since introduction); corrected to --theme-surface — autofill background/box-shadow were silently no-ops. - Add theming-form-geometry-tokens.md documenting the non-colour form/button/input token inventory; expand theming-partial-override.md's slot table to 11 tokens. - PricingCard: add optional ribbonText prop for a diagonal corner-ribbon marketing callout, with story and stories.ts docs/args update.
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.
ActionMenu component
Adds a new
ActionMenumolecule comprising two components:ActionMenu(the trigger + popover container) andActionMenuItemCore(the individual row items).What
An ellipsis button (
lucide:ellipsis) that opens an anchored popover menu positioned below and right-aligned with the trigger. Each menu item is passed via an indexed dynamic slot (item-{n}) containing a singleActionMenuItemCore, which renders as a<button>,<NuxtLink>(internal/paths), or<a>(external URLs) depending on whetherhrefis supplied.Why a new component rather than extending
InputButtonCoreInputButtonCoreis a form primitive — it carries form-specific props (isPending,isPill,readonly,type) and markup assumptions that don't belong on menu items.ActionMenuItemCoreduplicates only the minimal link/button resolution logic (4 computed lines) and has a purpose-built API:label(required),href(optional),#iconslot, and aclickemit.Decisions
defaultslot —item-{n}slots enforce that onlyActionMenuItemCorecontent enters the<ul role="menu">, keeping the ARIAmenu/menuitemtree intact and preventing arbitrary HTML from breaking the accessible structure. Consistent with theAccordianCorepattern already in the codebase.right: anchor(right)), flipping above viaposition-try-fallbacks: flip-blockwhen near the bottom of the viewport.useId()for the anchor name — the anchor name is generated internally as a--dashed-ident(--action-menu-anchor-{id}) so consumers never need to pass an ID prop.Home/Endjump to first/last,Tabcloses the menu (letting focus move naturally to the next DOM element),Escapeis handled natively by the Popover API which also restores focus to the trigger. No focus trap —focus-trap-vuecycles Tab within the element, which is correct for dialogs but wrong for menus.closeMenureturns focus to the trigger — when an item is activated by click, focus is explicitly returned to the trigger. Escape-close handles this automatically via the Popover API's built-in focus restoration.Files
ActionMenu.vueitem-{n}slot loop, keyboard handlerActionMenuItemCore.vueCONSUMER-STYLING.md.claude/skills/components/action-menu.mdActionMenu.stories.tsActionMenu.spec.tsActionMenuItemCore.spec.tsCSS tokens
All
--action-menu-*and--action-menu-item-*tokens are documented inCONSUMER-STYLING.md. Recommended approach is a single global file (assets/styles/setup/07.components/action-menu.css) on:root, consistent withDisplayDialogandTabNavigation.Test notes
Keyboard focus tests use
vi.spyOn(el, 'focus')andvi.spyOn(document, 'activeElement', 'get')rather than assertingdocument.activeElementdirectly — jsdom does not honourfocus()on elements inside a hidden popover (display: none), so spying on the call is the reliable approach.61 tests passing, 0 failing.