Skip to content

Conversation

castastrophe
Copy link
Collaborator

@castastrophe castastrophe commented Jul 29, 2025

Description

This PR:

  • Adds a new @spectrum-css/actionmenu component that composes ActionButton, Popover, and Menu to present action lists from a trigger.
  • Updates @spectrum-css/actionbutton and @spectrum-css/actiongroup to treat selection via .is-selected as well as :where([aria-pressed="true"], [aria-expanded="true"]) to cover more accessibility use-cases while keeping selector specificity low.
  • Refines @spectrum-css/menu to align with Spectrum 2 specifications and accessibility improvements.

Design references:

  • Figma S2 token specs: link

Includes a changeset with the following bumps:

  • @spectrum-css/actionmenu: major (new component)
  • @spectrum-css/actionbutton: minor (selection semantics with ARIA via :where)
  • @spectrum-css/menu: patch (S2 refinements, accessibility)
  • @spectrum-css/actiongroup: patch (selection semantics alignment)

How and where has this been tested?

  • Storybook locally:
    • Verified Action menu stories (default, long-press, placements).
    • Verified Menu stories for focus indicators, CJK line-height, external link/drill-in icons, thumbnails, and forced-colors behavior.
    • Verified Action button and Action group selected visuals using .is-selected, [aria-pressed="true"], and [aria-expanded="true"].
  • Forced Colors (Windows High Contrast) visual pass in Storybook.
  • Chromatic/VRT pending reviewer confirmation.

Validation steps

  1. Open Storybook for Action menu:
    • Toggle isOpen and confirm popover/menu spacing and placement reflect updates.
    • Enable “Long press” and confirm press vs long-press behavior in docs.
  2. Open Storybook for Action button:
    • Toggle .is-selected, set aria-pressed="true", and aria-expanded="true"; confirm identical visuals due to :where(...).
  3. Open Storybook for Action group:
    • In compact group, confirm hover/selected/focus ring layering and selection visuals via .is-selected and ARIA attributes.
  4. Open Storybook for Menu:
    • Confirm external-link and drill‑in icon sizing; thumbnail sizing/alignment.
    • Confirm forced-colors readability.

Screenshots

  • Add Storybook screenshots for:
    • Action menu default and long-press
    • Action button selected (class vs ARIA)
    • Menu focus and forced-colors states

To-do list

  • I have read the contribution guidelines.
  • I have updated relevant Storybook stories and templates.
  • I have tested these changes in Windows High Contrast mode.
  • If my change impacts other components, I have tested to make sure they don't break.
  • If my change impacts documentation, I have updated the documentation accordingly.
  • I have included a well-written changeset if my change needs to be published.

Notes for reviewers

  • No class name changes; selection semantics expanded via :where([aria-pressed],[aria-expanded]) to avoid specificity issues and broaden accessibility support.
  • Menu refinements include focus margin reservation, CJK line-height variables, transparent-at-rest backgrounds per S2, and forced-colors improvements.

References

  • Changesets documentation: https://github.com/changesets/changesets
  • Figma S2 token specs: https://www.figma.com/design/eoZHKJH9a3LJkHYCGt60Vb/S2-Token-specs?node-id=19758-3424

@castastrophe castastrophe changed the title feat(action-menu): S2 migration feat(action-menu): S2 migration [CSS-1160] Jul 29, 2025
Copy link
Contributor

github-actions bot commented Jul 29, 2025

📚 Branch preview

PR #4085 has been deployed to Azure Blob Storage: https://spectrumcss.z13.web.core.windows.net/pr-4085/index.html.

Copy link
Contributor

github-actions bot commented Jul 29, 2025

File metrics

Summary

Total size: 1.44 MB*
Total change (Δ): 🔴 ⬆ 0.83 KB (0.05%)

Table reports on changes to a package's main file. Other changes can be found in the collapsed Details section below.

Package Size Minified Gzipped Δ
actionbutton 23.60 KB 22.52 KB 2.99 KB 🟢 ⬇ 0.06 KB
actiongroup 7.73 KB 7.41 KB 1.09 KB 🔴 ⬆ 0.06 KB
actionmenu 0.84 KB 🆕 0.83 KB 🆕 0.48 KB 🆕 0.84 KB
menu 47.47 KB 45.21 KB 5.00 KB 🟢 ⬇ 0.45 KB

File change details

actionbutton

Filename Head Minified Gzipped Compared to base
index.css 23.60 KB 22.52 KB 2.99 KB 🟢 ⬇ 0.06 KB
metadata.json 10.41 KB - - 🔴 ⬆ 0.22 KB

actiongroup

Filename Head Minified Gzipped Compared to base
index.css 7.73 KB 7.41 KB 1.09 KB 🔴 ⬆ 0.06 KB
metadata.json 3.33 KB - - 🔴 ⬆ 0.06 KB

actionmenu

Filename Head Minified Gzipped Compared to base
index.css 0.84 KB 🆕 0.83 KB 🆕 0.48 KB 🆕 0.84 KB
metadata.json 0.31 KB - - 🆕 0.31 KB

menu

Filename Head Minified Gzipped Compared to base
index.css 47.47 KB 45.21 KB 5.00 KB 🟢 ⬇ 0.45 KB
metadata.json 23.43 KB - - 🟢 ⬇ 0.52 KB
* Size is the sum of all main files for packages in the library.
* An ASCII character in UTF-8 is 8 bits or 1 byte.

@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch from 2729f84 to 779e411 Compare July 30, 2025 20:23
Copy link

changeset-bot bot commented Jul 30, 2025

🦋 Changeset detected

Latest commit: 03a7ae4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@spectrum-css/actionmenu Major
@spectrum-css/actionbutton Minor
@spectrum-css/menu Patch
@spectrum-css/actiongroup Patch
@spectrum-css/bundle Patch
@spectrum-css/preview Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 3 times, most recently from 635f709 to 242929c Compare August 1, 2025 20:06
@castastrophe castastrophe marked this pull request as ready for review August 1, 2025 20:06
@castastrophe castastrophe self-assigned this Aug 1, 2025
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 2 times, most recently from 4baa099 to eb8cde2 Compare August 4, 2025 15:23
@castastrophe castastrophe added size-2 S ~6-18hrs; not hard or time consuming, one or two work days to complete. wip This is a work in progress, don't judge. run_vrt For use on PRs looking to kick off VRT S2 Spectrum 2 labels Aug 4, 2025
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 4 times, most recently from 313ee43 to 3babcfa Compare August 6, 2025 23:43
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 2 times, most recently from 4d618da to f3fa4d4 Compare August 7, 2025 16:14
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 3 times, most recently from f2ef53c to 409436c Compare August 18, 2025 16:08
@adobe adobe deleted a comment from github-actions bot Aug 18, 2025
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 2 times, most recently from dbd613f to 00038a9 Compare August 18, 2025 17:12
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 7 times, most recently from e4bf910 to 46a8a1b Compare September 19, 2025 17:14
}

/* Focus indicator */
.spectrum-ActionButton {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just me tidying up a bit by combining these styles in with the initial definition for .spectrum-ActionButton

.spectrum-Menu-itemCheckbox {
grid-area: checkmarkArea;
}
&.is-selectableMultiple:not(:has(.is-selectable)) .spectrum-Menu-itemCheckbox {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added this not to prevent clash with the is-selectable placement.

@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch from 46a8a1b to 75c284c Compare September 19, 2025 17:28
Comment on lines 130 to 133
* By default, the menu is opened by pressing the trigger element or activating it via the <kbd>Space</kbd> or <kbd>Enter</kbd> keys. However, there may be cases where the trigger should perform a separate action on press such as selection, and should only display the menu when long pressed. For this use-case, the menu will only be opened when pressing and holding the trigger or by using the <kbd>Option</kbd> (Alt on Windows) + <kbd>Down arrow</kbd>/<kbd>Up arrow</kbd> keys while focusing the trigger.
*
* This example illustrates the expected visuals and states of the action menu for a trigger with both long press and short press behaviors.
*/
Copy link
Collaborator

Choose a reason for hiding this comment

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

😍 love this description!

I don't see the long press working here (I can't access the menu for long press), but I don't think we expect it to, is that correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I took a first pass at making the long-press functional and then thought, this feels a little out-of-scope. Do you think it's worth making it work or should I just note that it doesn't work in CSS but does in SWC?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I noticed what I thought was maybe a regression for the Media Options story on the Coachmark docs page:
image

The container has a height being set on it in the customStyles, wondering if removing that would help?

I couldn't figure out what would have introduced it here though, and I assumed it was a regression from something in this branch because I don't see it in the spectrum-two branch preview.

But I do see the same issue when I run spectrum-two locally so I'm wondering if it's something from #4174 maybe, since it looks like that's where the need for some of those explicit dimensions on popover was eliminated?

Copy link
Collaborator

Choose a reason for hiding this comment

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

As a side note, I also have been noticing some weirdness here, but it's inconsistent so I'm not sure what we should do about it. Sometimes when I open the coachmark component story (on local or preview) the height or sometimes width of the component increases infinitely. It doesn't happen every time, but I'm thinking it's triggered if I open the Coachmark Default story after the Action menu story (I was able to replicate in Firefox, Chrome, and Safari).

image

It also happens in popover, also if I click into the Popover story after Action menu:

image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay, Steph saw some of that too until she'd done a hard refresh. I wonder if it's an inconsistent bug.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Try this and see if you can replicate it, this is what I'm finding that breaks it (regardless of browser):

  • Open the Action menu component in Storybook (Docs page or Default story, it doesn't matter, as long as the popover is open, this won't happen if the action menu popover stays closed)
  • Open either the Coach mark component or the Popover component in Storybook (Docs page or Default story)
  • Expect to see either the height or width of the popover there expand infinitely until you refresh

I think there's something about Action menu, because I can go between Coach mark and Popover just fine.

Another thing I noticed is that if I start on the Popover with position bottom, then go to Action menu where bottom isn't a valid option (there's only bottom-start and bottom-end), then go back to Popover, the popover ends up in a completely different position. Unsure if that's related or not, but something I noticed.
image

Unless you've got an idea on how to address it, I'm cool with a follow-up ticket.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I've finally got this resolved, would you mind validating on your system as well to be sure? Thank you!

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am still seeing the issue when navigating from an open action menu to something like popover or coachmark without refreshing, both locally and in the PR preview 😕

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see the accessibility improvements that are mentioned in the changeset for menu but I don't really see updates to some of these other items (forced-colors, focus indicator, cjk), am I looking in the right place?

Menu refinements

Updates @spectrum-css/menu styles to align with latest Spectrum 2 design specifications and improve accessibility.

  • Focus indicator tokens wired through: width, color, gap/offset, and outline style.
  • CJK line-height tokens applied for labels, descriptions, and section headers.
  • External link and drill‑in icon sizing variables exposed; thumbnail sizing and alignment refined.
  • Forced-colors improvements and readability adjustments.
  • Non-breaking; no class or DOM changes required.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm I wonder if I pulled some of that back and forgot to update the changeset. I'll double check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated now!


/*
* @spectrum-css/actionmenu
* This component is a combination of a menu, popover, and action button.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is more related to popover, which wasn't changed in this PR:

I see the button jumping a bit (for bottom-start it moves down and to the right) when I click it, I think it's related to the previous popover positioning (alliteration always!) work and doesn't seem intentional:

Screen.Recording.2025-09-25.at.12.01.12.PM.mov

If I pull down spectrum-two, I can also see the issue there:

Screen.Recording.2025-09-25.at.12.08.30.PM.mov

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just noting I still see this jumping around! Sadly I don't think the video I attached previously is working, I'm going to try a gif version instead!

I'm seeing:

  • The action button jumps from the middle of the window to more of an upper-left position (only if I reload this from a closed position though)
  • Smaller shakes every time I reopen it from a closed position.

Image

@castastrophe castastrophe added size-3 M ~18-30hrs; moderate effort or complexity, several work days needed. skip_vrt Add to a PR to skip running VRT (but still pass the action) design-approved and removed size-2 S ~6-18hrs; not hard or time consuming, one or two work days to complete. run_vrt For use on PRs looking to kick off VRT labels Sep 25, 2025
Copy link
Contributor

@5t3ph 5t3ph left a comment

Choose a reason for hiding this comment

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

Not sure if you consider this in-scope but you called it out for testing - maybe for the compact action group the focus radius should be reduced?

image

&.is-selected {
/* expanded is specific to action menu when the menu is open */
&:where(.is-selected, [aria-pressed="true"], [aria-expanded="true"]) {
--mod-actionbutton-background-color-default: var(--mod-actionbutton-background-color-default-selected, var(--spectrum-neutral-background-color-selected-default));
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this update component properties instead of mods, or is it a case where it hits an issue going into SWC environment?

Either way - a good case to flag for our upcoming discussions around this 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

That said - I also don't see this change in the Action Menu tokens, possibly because "selected" is a different state/intent than "expanded"? Should we double-check with design if this should take on the darker background?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, action button should be our first attempt to introduce layers I suspect because it is riddled with specificity battles and often hooking the modifiers was the only way to ensure properties updated in context.

Copy link
Contributor

Choose a reason for hiding this comment

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

got it, ok!

};

/**
* Action menus can be positioned in four locals relative to the trigger but <u>only one menu can be triggered at a single time</u>.
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] perhaps switch to <em> since underlines usually indicate a link?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Great point! Updating this now.

@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 2 times, most recently from 22ccfc9 to 2aed50b Compare September 29, 2025 18:04
@castastrophe
Copy link
Collaborator Author

Not sure if you consider this in-scope but you called it out for testing - maybe for the compact action group the focus radius should be reduced?

@5t3ph I opened an issue for this and will get the question asked in the design channel! #4246

Copy link
Collaborator

@rise-erpelding rise-erpelding left a comment

Choose a reason for hiding this comment

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

I left more details on some of the popover issues I've been noticing, I'd love to hear your thoughts! I'm cool with these being a follow-up ticket if they can't be solved here.

Otherwise, just some minor adjustments needed, I think!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Try this and see if you can replicate it, this is what I'm finding that breaks it (regardless of browser):

  • Open the Action menu component in Storybook (Docs page or Default story, it doesn't matter, as long as the popover is open, this won't happen if the action menu popover stays closed)
  • Open either the Coach mark component or the Popover component in Storybook (Docs page or Default story)
  • Expect to see either the height or width of the popover there expand infinitely until you refresh

I think there's something about Action menu, because I can go between Coach mark and Popover just fine.

Another thing I noticed is that if I start on the Popover with position bottom, then go to Action menu where bottom isn't a valid option (there's only bottom-start and bottom-end), then go back to Popover, the popover ends up in a completely different position. Unsure if that's related or not, but something I noticed.
image

Unless you've got an idea on how to address it, I'm cool with a follow-up ticket.

imageIsFixedHeight: true,
imageSource: "example-card-portrait.png",
customStyles: {
"height": "315px"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this 315px set height is causing this issue on the Coachmark Docs page:

Image

&:disabled,
&.is-disabled {
/* ideal when we want to disable the button but still allow it's content to be focused */
&:where(:disabled, .is-disabled, [aria-disabled="true"]) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just noticed that I think this might be changing the specificity so that I see the text color change when I hover over the disabled action button 😱

Image Image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, updating that to use :is instead!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Docs page looks great!


/*
* @spectrum-css/actionmenu
* This component is a combination of a menu, popover, and action button.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just noting I still see this jumping around! Sadly I don't think the video I attached previously is working, I'm going to try a gif version instead!

I'm seeing:

  • The action button jumps from the middle of the window to more of an upper-left position (only if I reload this from a closed position though)
  • Smaller shakes every time I reopen it from a closed position.

Image

Comment on lines 43 to 48
### Menu refinements

Updates `@spectrum-css/menu` styles to align with latest Spectrum 2 design specifications and improve accessibility.

- Focus indicator tokens wired through: width, color, gap/offset, and outline style.
- CJK line-height tokens applied for labels, descriptions, and section headers.
- External link and drill‑in icon sizing variables exposed; thumbnail sizing and alignment refined.
- Forced-colors improvements and readability adjustments.
- Non-breaking; no class or DOM changes required.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I can't find the comment off the top of my head, but did we talk about updating this part of the changeset to more accurately reflect menu changes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes! I had it stashed locally and hadn't pushed it up

@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch 2 times, most recently from 0835cae to 41b6453 Compare October 2, 2025 16:25
@castastrophe castastrophe force-pushed the castastrophe/feat-action-menu-migration branch from 41b6453 to 03a7ae4 Compare October 2, 2025 18:15
Copy link
Collaborator

@rise-erpelding rise-erpelding left a comment

Choose a reason for hiding this comment

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

I’m still seeing some issues with Action Menu/Popover. I’ve documented most inline and tested across multiple browsers, so hopefully they’re reproducible on your end.

The CSS looks solid overall, though! I wonder if simplifying some of the JS implementation could help stabilize things so we can ship this.

function resizeObserverCallback(entries) {
for (const entry of entries) {
const isPopover = entry.target === popoverEl;
const size = entry.contentRect;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I noticed that the spacing between the action button and menu looked slim.

Image

I'm wondering if this is because --spectrum-popover-trigger-height is a bit short? It's 28px but the action button is 32px, I guess because entry.contentRect only accounts for content area? I'm not sure if either trying to account for the padding & border in the transform would help, or if it would be better to use something like entry.borderBoxSize[0].blockSize?

Edit: I also am seeing this in the other positions, it's more noticeable with the start-top position, for example, because the popover is overlapping the button:

Image

Comment on lines +74 to +84
if (typeof onclick !== "function") {
document.body.addEventListener("click", function (evt) {
if (evt.target.closest(`.${rootClass}`)) {
return;
}
updateArgs({
isSelected: false,
isOpen: false,
});
});
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I notice that this is causing some problems in the action button story (I get some version of this error in Chrome/Safari/Firefox when running locally):

Image

But even if you do some kind of null check for updateArgs, Chrome also doesn't love the way the event listener is being added.

Image

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am still seeing the issue when navigating from an open action menu to something like popover or coachmark without refreshing, both locally and in the PR preview 😕

customWrapperStyles["align-items"] = "end";
}
else if (position.startsWith("bottom") || position.endsWith("-top")) {
customWrapperStyles["align-items"] = "start";
Copy link
Collaborator

Choose a reason for hiding this comment

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

I've also been noticing that the inline styles that are being set here are leaking over to components that don't need them/don't have popover, for instance in action button, if you navigate to the story after going to action menu, it's still applying the align-items: start which, for this component, makes the icon move to the top of the button. (It does go away if you refresh.)

Image

This is also being applied unintentionally to other components if you navigate to them after going to action menu, for instance checkbox, or accordion:

Image Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design-approved ready-for-review S2 Spectrum 2 size-3 M ~18-30hrs; moderate effort or complexity, several work days needed. skip_vrt Add to a PR to skip running VRT (but still pass the action)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants