-
Notifications
You must be signed in to change notification settings - Fork 207
feat(action-menu): S2 migration [CSS-1160] #4085
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: spectrum-two
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
"@spectrum-css/actionmenu": major | ||
"@spectrum-css/actionbutton": minor | ||
"@spectrum-css/menu": patch | ||
"@spectrum-css/actiongroup": patch | ||
--- | ||
|
||
### Action menu component (now with custom styles!) | ||
|
||
Introduces `@spectrum-css/actionmenu`, a composition of `ActionButton`, `Popover`, and `Menu` to present action lists from a trigger. Now with custom styles! | ||
|
||
- Adds wrapper classes: `spectrum-ActionMenu`, `spectrum-ActionMenu-trigger`, `spectrum-ActionMenu-popover`, and `spectrum-ActionMenu-menu`. | ||
- Supports long press triggers and four placements (start/end, top/bottom) via the underlying popover API. | ||
- Design reference: [Figma S2 token specs](https://www.figma.com/design/eoZHKJH9a3LJkHYCGt60Vb/S2-Token-specs?node-id=19758-3424). | ||
|
||
#### Migration notes | ||
|
||
- If you previously composed an action menu manually (action button + popover + menu), you can adopt the new wrapper classes without changing the underlying markup semantics. Ensure the trigger has `aria-haspopup="menu"` and manages `aria-expanded` according to your application logic. | ||
- For spacing customizations previously done with ad‑hoc margins, switch to the new `--spectrum-actionmenu-button-to-menu-gap` custom property. | ||
|
||
Example markup: | ||
|
||
```html | ||
<div class="spectrum-ActionMenu"> | ||
<button | ||
class="spectrum-ActionMenu-trigger spectrum-ActionButton" | ||
aria-haspopup="menu" | ||
aria-expanded="false" | ||
> | ||
<!-- icon/label --> | ||
</button> | ||
<div class="spectrum-ActionMenu-popover spectrum-Popover"> | ||
<ul class="spectrum-ActionMenu-menu spectrum-Menu"> | ||
<!-- menu items --> | ||
</ul> | ||
</div> | ||
<!-- popover positioning/visibility is owned by your implementation --> | ||
<!-- use long-press behavior when appropriate to your UX --> | ||
<!-- use Popover placement options: bottom-start, bottom-end, start-top, end-top --> | ||
</div> | ||
``` | ||
|
||
### Menu refinements | ||
|
||
Updates `@spectrum-css/menu` styles to align with latest Spectrum 2 design specifications and improve accessibility. | ||
|
||
- Added this not to prevent clash with the `.is-selectable` placement. | ||
- Non-breaking; no class or DOM changes required. | ||
|
||
### Action button refinements | ||
|
||
- Selection styling now applies when components use ARIA pressed/expanded semantics, not just `.is-selected`. | ||
- Implemented with `:where()` to keep selector specificity low and prevent downstream specificity battles. | ||
- Non-breaking; no class changes required. | ||
|
||
### Action group refinements | ||
|
||
Aligns selection behavior of grouped items with action button updates. | ||
|
||
- Adds `:where([aria-pressed="true"], [aria-expanded="true"])` alongside `.is-selected` on items to cover more accessibility use-cases while keeping specificity low. | ||
- Non-breaking; no class changes required. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,7 @@ governing permissions and limitations under the License. | |
--spectrum-actionbutton-background-color-focus: var(--spectrum-gray-200); | ||
--spectrum-actionbutton-background-color-disabled: transparent; | ||
|
||
&.is-selected { | ||
&:is(.is-selected, [aria-pressed="true"], [aria-expanded="true"]) { | ||
--spectrum-actionbutton-background-color-disabled: var(--spectrum-disabled-background-color); | ||
} | ||
} | ||
|
@@ -116,7 +116,8 @@ governing permissions and limitations under the License. | |
} | ||
} | ||
|
||
&.is-selected { | ||
/* expanded is specific to action menu when the menu is open */ | ||
&:is(.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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it, ok! |
||
--mod-actionbutton-background-color-hover: var(--mod-actionbutton-background-color-hover-selected, var(--spectrum-neutral-background-color-selected-hover)); | ||
--mod-actionbutton-background-color-down: var(--mod-actionbutton-background-color-down-selected, var(--spectrum-neutral-background-color-selected-down)); | ||
|
@@ -298,6 +299,16 @@ governing permissions and limitations under the License. | |
border-style: none; | ||
} | ||
|
||
&::after { | ||
position: absolute; | ||
inset: 0; | ||
margin: calc((var(--spectrum-actionbutton-focus-indicator-gap) + var(--spectrum-actionbutton-border-width)) * -1); | ||
border-radius: var(--spectrum-actionbutton-focus-indicator-border-radius); | ||
transition: box-shadow var(--highcontrast-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration)) ease-in-out; | ||
pointer-events: none; | ||
content: ""; | ||
} | ||
|
||
&:focus { | ||
outline: none; | ||
} | ||
|
@@ -315,6 +326,13 @@ governing permissions and limitations under the License. | |
&:focus-visible { | ||
background-color: var(--highcontrast-actionbutton-background-color-default, var(--mod-actionbutton-background-color-focus, var(--spectrum-actionbutton-background-color-focus))); | ||
color: var(--highcontrast-actionbutton-content-color-default, var(--mod-actionbutton-content-color-focus, var(--spectrum-actionbutton-content-color-focus))); | ||
|
||
box-shadow: none; | ||
outline: none; | ||
|
||
&::after { | ||
box-shadow: 0 0 0 var(--spectrum-actionbutton-focus-indicator-thickness) var(--highcontrast-actionbutton-focus-indicator-color, var(--spectrum-actionbutton-focus-indicator-color)); | ||
} | ||
} | ||
|
||
&:active { | ||
|
@@ -323,8 +341,8 @@ governing permissions and limitations under the License. | |
transform: perspective(var(--spectrum-actionbutton-downstate-perspective)) translateZ(var(--spectrum-component-size-difference-down)); | ||
} | ||
|
||
&:disabled, | ||
&.is-disabled { | ||
/* ideal when we want to disable the button but still allow it's content to be focused */ | ||
&:is(:disabled, .is-disabled, [aria-disabled="true"]) { | ||
background-color: var(--highcontrast-actionbutton-background-color-disabled, var(--mod-actionbutton-background-color-disabled, var(--spectrum-actionbutton-background-color-disabled))); | ||
color: var(--highcontrast-actionbutton-content-color-disabled, var(--mod-actionbutton-content-color-disabled, var(--spectrum-actionbutton-content-color-disabled))); | ||
} | ||
|
@@ -364,10 +382,6 @@ a.spectrum-ActionButton { | |
/* Fixes horizontal alignment of text in anchor buttons */ | ||
text-align: center; | ||
|
||
&:empty { | ||
display: none; | ||
} | ||
|
||
pointer-events: none; | ||
|
||
font-size: var(--mod-actionbutton-font-size, var(--spectrum-actionbutton-font-size)); | ||
|
@@ -378,40 +392,21 @@ a.spectrum-ActionButton { | |
|
||
text-overflow: ellipsis; | ||
overflow: hidden; | ||
|
||
&:empty { | ||
display: none; | ||
} | ||
} | ||
|
||
.spectrum-ActionButton-hold { | ||
position: absolute; | ||
inset-inline-end: calc(var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-actionbutton-edge-to-hold-icon)) - var(--spectrum-actionbutton-border-width)); | ||
inset-block-end: calc(var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-actionbutton-edge-to-hold-icon)) - var(--spectrum-actionbutton-border-width)); | ||
display: block; | ||
color: inherit; | ||
transform: var(--spectrum-logical-rotation); | ||
} | ||
|
||
/* Focus indicator */ | ||
.spectrum-ActionButton { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
transition: border-color var(--highcontrast-actionbutton-animation-duration, var(--mod-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration))) ease-in-out; | ||
|
||
&::after { | ||
position: absolute; | ||
inset: 0; | ||
margin: calc((var(--mod-actionbutton-focus-indicator-gap, var(--spectrum-actionbutton-focus-indicator-gap)) + var(--spectrum-actionbutton-border-width)) * -1); | ||
border-radius: var(--mod-actionbutton-focus-indicator-border-radius, var(--spectrum-actionbutton-focus-indicator-border-radius)); | ||
transition: box-shadow var(--highcontrast-actionbutton-animation-duration, var(--mod-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration))) ease-in-out; | ||
pointer-events: none; | ||
content: ""; | ||
} | ||
|
||
&:focus-visible { | ||
box-shadow: none; | ||
outline: none; | ||
|
||
&::after { | ||
box-shadow: 0 0 0 var(--mod-actionbutton-focus-indicator-thickness, var(--spectrum-actionbutton-focus-indicator-thickness)) var(--highcontrast-actionbutton-focus-indicator-color, var(--mod-actionbutton-focus-indicator-color, var(--spectrum-actionbutton-focus-indicator-color))); | ||
} | ||
} | ||
} | ||
|
||
@media (forced-colors: active) { | ||
.spectrum-ActionButton { | ||
/** | ||
|
@@ -457,7 +452,7 @@ a.spectrum-ActionButton { | |
--highcontrast-actionbutton-background-color-disabled: Canvas; | ||
--highcontrast-actionbutton-content-color-default: CanvasText; | ||
|
||
&:disabled:not(.is-selected) { | ||
&:is(:disabled, .is-disabled, [aria-disabled="true"]):not(:where(.is-selected, [aria-pressed="true"], [aria-expanded="true"])) { | ||
--highcontrast-actionbutton-border-color: Canvas; | ||
} | ||
} | ||
|
@@ -469,8 +464,7 @@ a.spectrum-ActionButton { | |
--highcontrast-actionbutton-border-color: Highlight; | ||
} | ||
|
||
/* Selected always shows as a solid highlighted color. */ | ||
&.is-selected { | ||
&:is(.is-selected, [aria-pressed="true"], [aria-expanded="true"]) { | ||
--highcontrast-actionbutton-border-color: Highlight; | ||
--highcontrast-actionbutton-background-color-default: Highlight; | ||
--highcontrast-actionbutton-content-color-default: HighlightText; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is for S2 and we've agreed to remove modifiers, I removed the mods on any lines I needed to update for this change anyway.