|
| 1 | +# Action button migration roadmap |
| 2 | + |
| 3 | +## Component specifications |
| 4 | + |
| 5 | +### CSS |
| 6 | + |
| 7 | +<details> |
| 8 | +<summary>CSS selectors</summary> |
| 9 | + |
| 10 | +**Base component:** |
| 11 | + |
| 12 | +- `.spectrum-ActionButton` |
| 13 | + |
| 14 | +**Sizes:** |
| 15 | + |
| 16 | +- `.spectrum-ActionButton--sizeXS` |
| 17 | +- `.spectrum-ActionButton--sizeS` |
| 18 | +- `.spectrum-ActionButton--sizeL` |
| 19 | +- `.spectrum-ActionButton--sizeXL` |
| 20 | + |
| 21 | +**Variants and treatments:** |
| 22 | + |
| 23 | +- `.spectrum-ActionButton.spectrum-ActionButton--quiet` |
| 24 | +- `.spectrum-ActionButton.spectrum-ActionButton--staticWhite` |
| 25 | +- `.spectrum-ActionButton.spectrum-ActionButton--staticWhite.spectrum-ActionButton--quiet` |
| 26 | +- `.spectrum-ActionButton.spectrum-ActionButton--staticBlack` |
| 27 | +- `.spectrum-ActionButton.spectrum-ActionButton--staticBlack.spectrum-ActionButton--quiet` |
| 28 | + |
| 29 | +**Child elements:** |
| 30 | + |
| 31 | +- `.spectrum-ActionButton .spectrum-ActionButton-hold` |
| 32 | +- `.spectrum-ActionButton .spectrum-ActionButton-icon` |
| 33 | +- `.spectrum-ActionButton .spectrum-ActionButton-label` |
| 34 | +- `.spectrum-ActionButton-hold` |
| 35 | +- `.spectrum-ActionButton-icon` |
| 36 | +- `.spectrum-ActionButton-label` |
| 37 | +- `.spectrum-ActionButton-label:empty` |
| 38 | + |
| 39 | +**States:** |
| 40 | + |
| 41 | +- `.spectrum-ActionButton.is-disabled` |
| 42 | +- `.spectrum-ActionButton.is-selected` |
| 43 | +- `.spectrum-ActionButton:disabled` |
| 44 | +- `.spectrum-ActionButton:active` |
| 45 | +- `.spectrum-ActionButton:hover` |
| 46 | + |
| 47 | +**Selected state with variants:** |
| 48 | + |
| 49 | +- `.spectrum-ActionButton.is-selected.spectrum-ActionButton--emphasized` |
| 50 | +- `.spectrum-ActionButton.is-selected.spectrum-ActionButton--staticWhite` |
| 51 | +- `.spectrum-ActionButton.is-selected.spectrum-ActionButton--staticBlack` |
| 52 | +- `.spectrum-ActionButton.spectrum-ActionButton--quiet.is-selected` |
| 53 | +- `.spectrum-ActionButton.spectrum-ActionButton--quiet:disabled:not(.is-selected)` |
| 54 | + |
| 55 | +**Focus indicators:** |
| 56 | + |
| 57 | +- `.spectrum-ActionButton:focus` |
| 58 | +- `.spectrum-ActionButton:focus-visible` |
| 59 | +- `.spectrum-ActionButton:focus-visible:after` |
| 60 | +- `.spectrum-ActionButton::-moz-focus-inner` |
| 61 | +- `.spectrum-ActionButton:after` |
| 62 | + |
| 63 | +**Content detection:** |
| 64 | + |
| 65 | +- `.spectrum-ActionButton:has(.spectrum-ActionButton-icon)` |
| 66 | +- `.spectrum-ActionButton:not(:has(.spectrum-ActionButton-label))` |
| 67 | + |
| 68 | +**RTL support:** |
| 69 | + |
| 70 | +- `.spectrum-ActionButton:dir(rtl)` |
| 71 | + |
| 72 | +**Link variant:** |
| 73 | + |
| 74 | +- `a.spectrum-ActionButton` |
| 75 | + |
| 76 | +</details> |
| 77 | + |
| 78 | +<details> |
| 79 | +<summary>Passthroughs</summary> |
| 80 | + |
| 81 | +- `--mod-button-animation-duration` |
| 82 | +- `--mod-button-font-family` |
| 83 | + |
| 84 | +</details> |
| 85 | + |
| 86 | +<details> |
| 87 | +<summary>Modifiers</summary> |
| 88 | + |
| 89 | +**Sizing and spacing:** |
| 90 | + |
| 91 | +- `--mod-actionbutton-height` |
| 92 | +- `--mod-actionbutton-min-width` |
| 93 | +- `--mod-actionbutton-edge-to-visual` |
| 94 | +- `--mod-actionbutton-edge-to-visual-only` |
| 95 | +- `--mod-actionbutton-edge-to-text` |
| 96 | +- `--mod-actionbutton-edge-to-hold-icon` |
| 97 | +- `--mod-actionbutton-text-to-visual` |
| 98 | +- `--mod-actionbutton-icon-size` |
| 99 | + |
| 100 | +**Typography:** |
| 101 | + |
| 102 | +- `--mod-actionbutton-font-size` |
| 103 | +- `--mod-actionbutton-font-weight` |
| 104 | +- `--mod-actionbutton-font-style` |
| 105 | +- `--mod-actionbutton-line-height` |
| 106 | +- `--mod-actionbutton-label-color` |
| 107 | + |
| 108 | +**Border and radius:** |
| 109 | + |
| 110 | +- `--mod-actionbutton-border-radius` |
| 111 | +- `--mod-actionbutton-focus-indicator-border-radius` |
| 112 | + |
| 113 | +**Focus indicators:** |
| 114 | + |
| 115 | +- `--mod-actionbutton-focus-indicator-gap` |
| 116 | +- `--mod-actionbutton-focus-indicator-thickness` |
| 117 | +- `--mod-actionbutton-focus-indicator-color` |
| 118 | + |
| 119 | +**Animation:** |
| 120 | + |
| 121 | +- `--mod-actionbutton-animation-duration` |
| 122 | + |
| 123 | +**Default state colors:** |
| 124 | + |
| 125 | +- `--mod-actionbutton-background-color-default` |
| 126 | +- `--mod-actionbutton-content-color-default` |
| 127 | + |
| 128 | +**Hover state colors:** |
| 129 | + |
| 130 | +- `--mod-actionbutton-background-color-hover` |
| 131 | +- `--mod-actionbutton-content-color-hover` |
| 132 | + |
| 133 | +**Focus state colors:** |
| 134 | + |
| 135 | +- `--mod-actionbutton-background-color-focus` |
| 136 | +- `--mod-actionbutton-content-color-focus` |
| 137 | + |
| 138 | +**Active/down state colors:** |
| 139 | + |
| 140 | +- `--mod-actionbutton-background-color-down` |
| 141 | +- `--mod-actionbutton-content-color-down` |
| 142 | + |
| 143 | +**Disabled state colors:** |
| 144 | + |
| 145 | +- `--mod-actionbutton-background-color-disabled` |
| 146 | +- `--mod-actionbutton-content-color-disabled` |
| 147 | + |
| 148 | +**Selected state colors:** |
| 149 | + |
| 150 | +- `--mod-actionbutton-background-color-default-selected` |
| 151 | +- `--mod-actionbutton-content-color-default-selected` |
| 152 | +- `--mod-actionbutton-background-color-hover-selected` |
| 153 | +- `--mod-actionbutton-content-color-hover-selected` |
| 154 | +- `--mod-actionbutton-background-color-focus-selected` |
| 155 | +- `--mod-actionbutton-content-color-focus-selected` |
| 156 | +- `--mod-actionbutton-background-color-down-selected` |
| 157 | +- `--mod-actionbutton-content-color-down-selected` |
| 158 | + |
| 159 | +**Selected + emphasized state colors:** |
| 160 | + |
| 161 | +- `--mod-actionbutton-background-color-default-selected-emphasized` |
| 162 | +- `--mod-actionbutton-content-color-default-selected-emphasized` |
| 163 | +- `--mod-actionbutton-background-color-hover-selected-emphasized` |
| 164 | +- `--mod-actionbutton-content-color-hover-selected-emphasized` |
| 165 | +- `--mod-actionbutton-background-color-focus-selected-emphasized` |
| 166 | +- `--mod-actionbutton-content-color-focus-selected-emphasized` |
| 167 | +- `--mod-actionbutton-background-color-down-selected-emphasized` |
| 168 | +- `--mod-actionbutton-content-color-down-selected-emphasized` |
| 169 | + |
| 170 | +</details> |
| 171 | + |
| 172 | +### SWC |
| 173 | + |
| 174 | +<details> |
| 175 | +<summary>Attributes</summary> |
| 176 | + |
| 177 | +**Size:** |
| 178 | + |
| 179 | +- `size` (values: `xs`, `s`, `m`, `l`, `xl`) - button size, no default |
| 180 | + |
| 181 | +**Variants:** |
| 182 | + |
| 183 | +- `emphasized` - adds visual emphasis to selected state (boolean) |
| 184 | +- `quiet` - applies quiet styling (boolean) |
| 185 | +- `static-color` (values: `white`, `black`) - static color variant for use over backgrounds |
| 186 | + |
| 187 | +**Selection state:** |
| 188 | + |
| 189 | +- `selected` - whether the button is selected (boolean) |
| 190 | +- `toggles` - whether to automatically manage selected state on interaction and use aria-pressed (boolean) |
| 191 | + |
| 192 | +**Hold affordance:** |
| 193 | + |
| 194 | +- `hold-affordance` - shows corner triangle indicator for longpress action (boolean) |
| 195 | + |
| 196 | +**States:** |
| 197 | + |
| 198 | +- `active` - active/pressed state (inherited from ButtonBase) |
| 199 | +- `disabled` - disabled state (inherited from Focusable) |
| 200 | + |
| 201 | +**Content:** |
| 202 | + |
| 203 | +- `label` - accessible label (inherited from LikeAnchor) |
| 204 | + |
| 205 | +**Focus management:** |
| 206 | + |
| 207 | +- `autofocus` - auto-focus on load (inherited from Focusable) |
| 208 | +- `tabIndex` - tab index (inherited from Focusable) |
| 209 | + |
| 210 | +**Link behavior:** |
| 211 | + |
| 212 | +- `href` - makes button behave as link (inherited from LikeAnchor) |
| 213 | +- `target` (values: `_blank`, `_parent`, `_self`, `_top`) - link target (inherited from LikeAnchor) |
| 214 | +- `download` - download attribute (inherited from LikeAnchor) |
| 215 | +- `referrerpolicy` - referrer policy (inherited from LikeAnchor) |
| 216 | +- `rel` - link relationship (inherited from LikeAnchor) |
| 217 | + |
| 218 | +**Form behavior:** |
| 219 | + |
| 220 | +- `type` (values: `button`, `submit`, `reset`) - button type (inherited from ButtonBase) |
| 221 | +- `name` - form field name (inherited from ButtonBase) |
| 222 | +- `value` - button value, defaults to trimmed text content |
| 223 | + |
| 224 | +**ARIA:** |
| 225 | + |
| 226 | +- `role` - ARIA role, defaults to `button` |
| 227 | + |
| 228 | +</details> |
| 229 | + |
| 230 | +<details> |
| 231 | +<summary>Slots</summary> |
| 232 | + |
| 233 | +- Default slot - text label of the action button |
| 234 | +- `icon` - the icon to use for action button |
| 235 | + |
| 236 | +</details> |
| 237 | + |
| 238 | +## Comparison |
| 239 | + |
| 240 | +### DOM structure changes |
| 241 | + |
| 242 | +<details> |
| 243 | +<summary>Spectrum Web Components</summary> |
| 244 | + |
| 245 | +```html |
| 246 | +<!-- Hold affordance icon (when hold-affordance=true) --> |
| 247 | +<!-- Size-specific corner triangle icon, rendered first --> |
| 248 | +<sp-icon-corner-triangle300 |
| 249 | + class="hold-affordance spectrum-UIIcon-CornerTriangle{size}" |
| 250 | +></sp-icon-corner-triangle300> |
| 251 | + |
| 252 | +<!-- Icon slot --> |
| 253 | +<slot name="icon"></slot> |
| 254 | + |
| 255 | +<!-- Label --> |
| 256 | +<span id="label"> |
| 257 | + <slot></slot> |
| 258 | +</span> |
| 259 | +``` |
| 260 | + |
| 261 | +</details> |
| 262 | + |
| 263 | +<details> |
| 264 | +<summary>Legacy (CSS main branch)</summary> |
| 265 | + |
| 266 | +```html |
| 267 | +<button |
| 268 | + class="spectrum-ActionButton spectrum-ActionButton--sizeM" |
| 269 | + aria-pressed="false" |
| 270 | + disabled |
| 271 | +> |
| 272 | + <!-- Hold affordance icon (when hasPopup provided) --> |
| 273 | + <svg class="spectrum-Icon spectrum-ActionButton-hold">...</svg> |
| 274 | + |
| 275 | + <!-- Icon (when iconName provided) --> |
| 276 | + <svg class="spectrum-Icon spectrum-ActionButton-icon">...</svg> |
| 277 | + |
| 278 | + <!-- Label (when label provided and not hideLabel) --> |
| 279 | + <span class="spectrum-ActionButton-label">Label</span> |
| 280 | +</button> |
| 281 | +``` |
| 282 | + |
| 283 | +</details> |
| 284 | + |
| 285 | +<details> |
| 286 | +<summary>Spectrum 2 (CSS spectrum-two branch)</summary> |
| 287 | + |
| 288 | +```html |
| 289 | +<button |
| 290 | + class="spectrum-ActionButton spectrum-ActionButton--sizeM" |
| 291 | + aria-pressed="false" |
| 292 | + disabled |
| 293 | +> |
| 294 | + <!-- Hold affordance icon (when hasPopup provided) --> |
| 295 | + <svg class="spectrum-Icon spectrum-ActionButton-hold">...</svg> |
| 296 | + |
| 297 | + <!-- Icon (when iconName provided) --> |
| 298 | + <svg class="spectrum-Icon spectrum-ActionButton-icon">...</svg> |
| 299 | + |
| 300 | + <!-- Label (when label provided and not hideLabel) --> |
| 301 | + <span class="spectrum-ActionButton-label">Label</span> |
| 302 | +</button> |
| 303 | +``` |
| 304 | + |
| 305 | +</details> |
| 306 | + |
| 307 | +### CSS => SWC mapping |
| 308 | + |
| 309 | +#### Sizes |
| 310 | + |
| 311 | +| CSS selector | SWC attribute | Status | |
| 312 | +| -------------------------------- | ------------------------------- | ----------- | |
| 313 | +| `.spectrum-ActionButton` | Base component (default size M) | Implemented | |
| 314 | +| `.spectrum-ActionButton--sizeXS` | `size="xs"` | Implemented | |
| 315 | +| `.spectrum-ActionButton--sizeS` | `size="s"` | Implemented | |
| 316 | +| `.spectrum-ActionButton--sizeL` | `size="l"` | Implemented | |
| 317 | +| `.spectrum-ActionButton--sizeXL` | `size="xl"` | Implemented | |
| 318 | + |
| 319 | +#### Variants and treatments |
| 320 | + |
| 321 | +| CSS selector | SWC attribute | Status | |
| 322 | +| ---------------------------------------------------------------------------------------- | -------------------------------- | ----------- | |
| 323 | +| `.spectrum-ActionButton.spectrum-ActionButton--quiet` | `quiet` | Implemented | |
| 324 | +| `.spectrum-ActionButton.spectrum-ActionButton--staticWhite` | `static-color="white"` | Implemented | |
| 325 | +| `.spectrum-ActionButton.spectrum-ActionButton--staticBlack` | `static-color="black"` | Implemented | |
| 326 | +| `.spectrum-ActionButton.spectrum-ActionButton--staticWhite.spectrum-ActionButton--quiet` | `static-color="white"` + `quiet` | Implemented | |
| 327 | +| `.spectrum-ActionButton.spectrum-ActionButton--staticBlack.spectrum-ActionButton--quiet` | `static-color="black"` + `quiet` | Implemented | |
| 328 | + |
| 329 | +#### States |
| 330 | + |
| 331 | +| CSS selector | SWC attribute | Status | |
| 332 | +| -------------------------------------------------------------------------------- | ----------------------------------- | ----------- | |
| 333 | +| `.spectrum-ActionButton.is-selected` | `selected` | Implemented | |
| 334 | +| `.spectrum-ActionButton.is-disabled` | `disabled` | Implemented | |
| 335 | +| `.spectrum-ActionButton:disabled` | `disabled` | Implemented | |
| 336 | +| `.spectrum-ActionButton:active` | `active` | Implemented | |
| 337 | +| `.spectrum-ActionButton:hover` | Pseudo-state | Implemented | |
| 338 | +| `.spectrum-ActionButton.is-selected.spectrum-ActionButton--emphasized` | `selected` + `emphasized` | Implemented | |
| 339 | +| `.spectrum-ActionButton.is-selected.spectrum-ActionButton--staticWhite` | `selected` + `static-color="white"` | Implemented | |
| 340 | +| `.spectrum-ActionButton.is-selected.spectrum-ActionButton--staticBlack` | `selected` + `static-color="black"` | Implemented | |
| 341 | +| `.spectrum-ActionButton.spectrum-ActionButton--quiet.is-selected` | `quiet` + `selected` | Implemented | |
| 342 | +| `.spectrum-ActionButton.spectrum-ActionButton--quiet:disabled:not(.is-selected)` | `quiet` + `disabled` (not selected) | Implemented | |
| 343 | + |
| 344 | +#### Focus indicators |
| 345 | + |
| 346 | +| CSS selector | SWC attribute | Status | |
| 347 | +| -------------------------------------------- | --------------------- | ----------- | |
| 348 | +| `.spectrum-ActionButton:focus` | Pseudo-state | Implemented | |
| 349 | +| `.spectrum-ActionButton:focus-visible` | Pseudo-state | Implemented | |
| 350 | +| `.spectrum-ActionButton:focus-visible:after` | Focus ring | Implemented | |
| 351 | +| `.spectrum-ActionButton:after` | Focus ring element | Implemented | |
| 352 | +| `.spectrum-ActionButton::-moz-focus-inner` | Firefox focus styling | Implemented | |
| 353 | + |
| 354 | +#### Content and layout |
| 355 | + |
| 356 | +| CSS selector | SWC attribute/slot | Status | |
| 357 | +| ---------------------------------------------------------------- | ---------------------------------------- | ----------- | |
| 358 | +| `.spectrum-ActionButton .spectrum-ActionButton-icon` | `icon` slot | Implemented | |
| 359 | +| `.spectrum-ActionButton-icon` | `icon` slot | Implemented | |
| 360 | +| `.spectrum-ActionButton .spectrum-ActionButton-label` | Default slot | Implemented | |
| 361 | +| `.spectrum-ActionButton-label` | Default slot | Implemented | |
| 362 | +| `.spectrum-ActionButton-label:empty` | Empty label handling | Implemented | |
| 363 | +| `.spectrum-ActionButton .spectrum-ActionButton-hold` | `hold-affordance` attribute | Implemented | |
| 364 | +| `.spectrum-ActionButton-hold` | `hold-affordance` attribute | Implemented | |
| 365 | +| `.spectrum-ActionButton:has(.spectrum-ActionButton-icon)` | Detected when icon slot has content | Implemented | |
| 366 | +| `.spectrum-ActionButton:not(:has(.spectrum-ActionButton-label))` | Detected when default slot is empty | Implemented | |
| 367 | +| `.spectrum-ActionButton:dir(rtl)` | Browser RTL support | Implemented | |
| 368 | +| `a.spectrum-ActionButton` | `href` attribute makes it render as link | Implemented | |
| 369 | + |
| 370 | +#### WC-only attributes |
| 371 | + |
| 372 | +| SWC attribute | CSS equivalent | Notes | |
| 373 | +| ------------- | -------------- | --------------------------------------------- | |
| 374 | +| `toggles` | N/A | Manages selected state automatically on click | |
| 375 | +| `value` | N/A | Used for identification in action groups | |
| 376 | +| `role` | N/A | Dynamic ARIA role management | |
| 377 | + |
| 378 | +## Summary of changes |
| 379 | + |
| 380 | +### CSS => SWC implementation gaps |
| 381 | + |
| 382 | +**Missing from WC:** |
| 383 | +None. All CSS selectors have corresponding web component implementations. |
| 384 | + |
| 385 | +### CSS Spectrum 2 changes |
| 386 | + |
| 387 | +**No structural changes:** |
| 388 | +The Action Button template is functionally identical between the main branch (legacy) and spectrum-two branch (Spectrum 2). Both branches render the same HTML DOM structure and use the same size-specific corner triangle icons (CornerTriangle75, CornerTriangle100, CornerTriangle200, CornerTriangle300). |
| 389 | + |
| 390 | +## Resources |
| 391 | + |
| 392 | +- [CSS migration](https://github.com/adobe/spectrum-css/pull/2669) |
| 393 | +- [Spectrum 2 preview](https://spectrumcss.z13.web.core.windows.net/pr-2352/index.html?path=/docs/components-action-button--docs) |
| 394 | +- [React](https://react-spectrum.adobe.com/s2/index.html?path=/docs/actionbutton--docs) |
0 commit comments