From 0df4e0390acea057079b4d4e3371f2c91e941207 Mon Sep 17 00:00:00 2001
From: Cory Dransfeldt <445732+cdransf@users.noreply.github.com>
Date: Tue, 26 May 2026 14:58:21 -0700
Subject: [PATCH 1/2] feat(action-button): adds migration plan for the S2
action button component (#6327)
* feat(action-button): adds migration plan for the S2 action button component
* chore(action-button): address feedback
* chore(action-button): address feedback
---
.../03_components/README.md | 1 +
.../action-button/migration-plan.md | 677 ++++++++++++++++++
2 files changed, 678 insertions(+)
create mode 100644 CONTRIBUTOR-DOCS/03_project-planning/03_components/action-button/migration-plan.md
diff --git a/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md b/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md
index a710563fe4a..cde58b33c4f 100644
--- a/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md
+++ b/CONTRIBUTOR-DOCS/03_project-planning/03_components/README.md
@@ -24,6 +24,7 @@
- [Accordion migration roadmap](accordion/rendering-and-styling-migration-analysis.md)
- Action Button
- [Action button accessibility migration analysis](action-button/accessibility-migration-analysis.md)
+ - [Action button migration plan](action-button/migration-plan.md)
- [Action button migration roadmap](action-button/rendering-and-styling-migration-analysis.md)
- Action Group
- [Action group migration roadmap](action-group/rendering-and-styling-migration-analysis.md)
diff --git a/CONTRIBUTOR-DOCS/03_project-planning/03_components/action-button/migration-plan.md b/CONTRIBUTOR-DOCS/03_project-planning/03_components/action-button/migration-plan.md
new file mode 100644
index 00000000000..f0471a64189
--- /dev/null
+++ b/CONTRIBUTOR-DOCS/03_project-planning/03_components/action-button/migration-plan.md
@@ -0,0 +1,677 @@
+
+
+[CONTRIBUTOR-DOCS](../../../README.md) / [Project planning](../../README.md) / [Components](../README.md) / Action Button / Action button migration plan
+
+
+
+# Action button migration plan
+
+
+
+
+In this doc
+
+- [TL;DR](#tldr)
+ - [Most blocking open questions](#most-blocking-open-questions)
+- [1st-gen API surface](#1st-gen-api-surface)
+ - [Properties / attributes](#properties--attributes)
+ - [Methods](#methods)
+ - [Events](#events)
+ - [Slots](#slots)
+ - [CSS custom properties](#css-custom-properties)
+ - [Shadow DOM output (rendered HTML)](#shadow-dom-output-rendered-html)
+- [Dependencies](#dependencies)
+- [Migration sequencing and prerequisites](#migration-sequencing-and-prerequisites)
+ - [Prerequisites](#prerequisites)
+ - [Dependencies this migration creates](#dependencies-this-migration-creates)
+ - [`spectrum-css` setup](#spectrum-css-setup)
+ - [Recommended order](#recommended-order)
+- [Changes overview](#changes-overview)
+ - [Must ship — breaking or a11y-required](#must-ship--breaking-or-a11y-required)
+ - [Additive — ships when ready, zero breakage for consumers already on 2nd-gen](#additive--ships-when-ready-zero-breakage-for-consumers-already-on-2nd-gen)
+- [2nd-gen API decisions](#2nd-gen-api-decisions)
+ - [Public API](#public-api)
+ - [Visual matrix](#visual-matrix)
+ - [Pending state (new in 2nd-gen)](#pending-state-new-in-2nd-gen)
+ - [Accessibility semantics notes (2nd-gen)](#accessibility-semantics-notes-2nd-gen)
+ - [Deferred semantics note (2nd-gen)](#deferred-semantics-note-2nd-gen)
+- [Architecture: core vs SWC split](#architecture-core-vs-swc-split)
+ - [Shared semantics reuse](#shared-semantics-reuse)
+- [Migration checklist](#migration-checklist)
+ - [Preparation (this ticket)](#preparation-this-ticket)
+ - [Setup](#setup)
+ - [API](#api)
+ - [Styling](#styling)
+ - [Accessibility](#accessibility)
+ - [Testing](#testing)
+ - [Documentation](#documentation)
+ - [Review](#review)
+- [Blockers and open questions](#blockers-and-open-questions)
+ - [Design](#design)
+ - [Architecture and behavior](#architecture-and-behavior)
+ - [Scope and prerequisites](#scope-and-prerequisites)
+ - [Resolved decisions](#resolved-decisions)
+ - [Deferred follow-up tickets](#deferred-follow-up-tickets)
+- [Breaking changes to verify](#breaking-changes-to-verify)
+- [References](#references)
+
+
+
+
+
+> **SWC-2039** · Planning output. Must be reviewed before implementation begins.
+
+---
+
+## TL;DR
+
+- `swc-action-button` extends `ButtonBase` from `2nd-gen/packages/core/components/button/` — no separate `ActionButton.base.ts` in core is needed since no 2nd-gen component inherits from `swc-action-button`
+- `selected`, `toggles`, and `aria-pressed` are removed from `swc-action-button`; toolbar-style toggles move to `swc-toggle-button` / `swc-toggle-button-group`
+- `emphasized` is removed because it only applies to the selected state, which is removed
+- `hold-affordance` / `longpress` are deferred until a later date; Storybook and migration copy must say so explicitly
+- `href` / link API is removed entirely; navigation uses native `` elements
+- `pending` ships with the initial release; `ButtonBase` provides the logic and the visual implementation can be copied from `swc-button`
+- `accessible-label` replaces `label` (inherited from `ButtonBase`)
+- `size` includes `xs` (not available on `swc-button`), requiring `ACTION_BUTTON_VALID_SIZES` in `ActionButton.types.ts`
+- `quiet` and `static-color` are retained as the primary visual differentiators for this component
+- `value` is **confirmed**; retained for identification within action groups (JS property defaults to `''`; falls back to `textContent` when empty)
+
+### Most blocking open questions
+
+- **Q2** — Consumer migration copy for `swc-action-button` references `swc-toggle-button` / `swc-toggle-button-group`, which are not yet available in 2nd-gen. Either hold the migration-guide note or add an explicit "coming soon" caveat before documenting the toggle migration path.
+- **Q1** (provisional, not blocking implementation) — The Figma PNG from `S2 / Web (Desktop scale)` has not been provided. The visual matrix in this plan is inferred from analysis docs. Confirm before Phase 5 (styling).
+
+---
+
+## 1st-gen API surface
+
+**Source:** [`1st-gen/packages/action-button/src/ActionButton.ts`](../../../../1st-gen/packages/action-button/src/ActionButton.ts)
+**Version:** `@spectrum-web-components/action-button@1.12.1`
+**Custom element tag:** `sp-action-button`
+**Extends:** `SizedMixin(ButtonBase, { validSizes: ['xs', 's', 'm', 'l', 'xl'], noDefaultSize: true })`
+
+### Properties / attributes
+
+| Property | Type | Default | Attribute | Notes |
+|---|---|---|---|---|
+| `emphasized` | `boolean` | `false` | `emphasized` | Adds visual emphasis to the selected state only. |
+| `holdAffordance` | `boolean` | `false` | `hold-affordance` | Shows a corner triangle indicator; wires 300 ms pointer timer and keyboard path for `longpress`. |
+| `quiet` | `boolean` | `false` | `quiet` | Applies quiet styling (no background/border at rest). |
+| `role` | `string` | `'button'` | `role` | Overrides the ARIA role. Used by 1st-gen `sp-action-group` to reassign `radio` / `checkbox` on children. |
+| `selected` | `boolean` | `false` | `selected` | Whether the button is selected. Managed by `toggles` or set externally. |
+| `toggles` | `boolean` | `false` | `toggles` | When true, automatically manages `selected` on click and exposes `aria-pressed`. |
+| `staticColor` | `'white' \| 'black' \| undefined` | `undefined` | `static-color` | Static color variant for use over images or colored backgrounds. |
+| `value` | `string` | `''` (falls back to `textContent`) | `value` | Used for identification in action groups. |
+| `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | `'m'` (no attribute) | `size` | T-shirt sizes; no default attribute from `noDefaultSize: true`. |
+| `disabled` | `boolean` | `false` | `disabled` | Inherited from `Focusable`; removes focusability. |
+| `autofocus` | `boolean` | `false` | `autofocus` | Inherited from `Focusable`. |
+| `tabIndex` | `number` | managed | `tabindex` | Inherited from `Focusable`. |
+| `label` | `string \| undefined` | `undefined` | `label` | Inherited from `LikeAnchor`; mirrored to host `aria-label`. Accessible name for icon-only usage. |
+| `href` | `string \| undefined` | `undefined` | `href` | Deprecated link mode (dev warning in 1st-gen). |
+| `target` | link target union | `undefined` | `target` | Deprecated with `href`. |
+| `download` | `string \| undefined` | `undefined` | `download` | Deprecated with `href`. |
+| `referrerpolicy` | `string \| undefined` | `undefined` | `referrerpolicy` | Deprecated with `href`. |
+| `rel` | `string \| undefined` | `undefined` | `rel` | Deprecated with `href`. |
+| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | `type` | Inherited from `ButtonBase`. |
+| `name` | `string` | `undefined` | `name` | Inherited from `ButtonBase`; form field name. |
+| `active` | `boolean` | `false` | `active` | Reflected pressed-state flag used during keyboard hold interaction. |
+
+### Methods
+
+| Method | Signature | Notes |
+|---|---|---|
+| `click()` | `(): void` | Delegates to `Focusable.click()`; no special pending guard in 1st-gen (1st-gen has no pending state). |
+
+### Events
+
+| Event | Detail | Notes |
+|---|---|---|
+| `change` | `Event` (cancelable) | Fired when `selected` changes via `toggles`. Calling `preventDefault()` reverts `selected`. |
+| `longpress` | `CustomEvent<{ source: 'pointer' \| 'keyboard' }>` | Fired after 300 ms pointer hold or `Space` / `Alt+ArrowDown` key hold when `hold-affordance` is set. |
+
+### Slots
+
+| Slot | Content | Notes |
+|---|---|---|
+| default | Visible text label | Required for accessible name unless `label` / `accessible-label` is set. |
+| `icon` | Leading icon element | Optional; component detects icon presence via slot content inspection. |
+
+### CSS custom properties
+
+The 1st-gen implementation uses `--spectrum-actionbutton-*` and `--mod-actionbutton-*` token chains via imported `action-button.css`, `spectrum-action-button.css`, and `action-button-overrides.css`. The modifier surface covers sizing and spacing, typography, and a full color/state/variant matrix. This full modifier surface will **not** be carried forward to 2nd-gen. A small reviewed set of `--swc-action-button-*` properties will be exposed instead.
+
+### Shadow DOM output (rendered HTML)
+
+```html
+
+
+
+
+
+
+
+
+
+
+```
+
+The host element itself carries `role="button"` and is the primary focus target. There is no inner native `