Skip to content

docs(infield-button): add Phase 1 migration plan#6441

Open
Rajdeepc wants to merge 6 commits into
mainfrom
rajdeepc/docs-infield-button-migration-plan
Open

docs(infield-button): add Phase 1 migration plan#6441
Rajdeepc wants to merge 6 commits into
mainfrom
rajdeepc/docs-infield-button-migration-plan

Conversation

@Rajdeepc

@Rajdeepc Rajdeepc commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the Phase 1 (prep) migration plan for swc-infield-button under CONTRIBUTOR-DOCS/03_project-planning/03_components/infield-button/, completing the planning artifact set alongside the existing rendering/styling and accessibility migration analyses.

The plan documents the 1st-gen API surface, breaking changes, 2nd-gen API decisions, the core/SWC architecture split, and the full Setup → API → Styling → Accessibility → Testing → Documentation → Review checklist. It follows the migration-prep template and is structurally aligned with the button and action-button plans.

Highlights

  • API: quiet + four sizes (s/m/l/xl, noDefaultSize: true), extends 2nd-gen ButtonBase; link API, block/inline, pending, and --mod-* removed; labelaccessible-label; icon moves to the named icon slot.
  • Design/CSS inputs confirmed: validated against the Figma S2 In-field button spec (node 126176-34080) and the spectrum-two components/infieldbutton/index.css baseline (sizes, states, quiet, forced-colors, no stacked/:lang() classes).
  • Styling: token mapping to token() + --swc-infield-button-*, slotted-icon styling via ::slotted, parent-field-owned focus model and inline-group wrapper.
  • Updates the components README navigation.

Open questions (tracked in the plan)

These are team decisions and intentionally remain open before implementation begins:

  • Q3: confirm sequencing (infield-button before number-field)
  • Q4: keep swc-infield-button standalone vs. merge with clear/close-button
  • Q5: ownership of the "clear field" affordance

Type of change

  • Documentation (planning artifact only; no production code or component behavior changes)

Related issue(s)

SWC-2147 (Epic: SWC-2107)

Test plan

  • Plan reviewed by at least one other engineer

Adds the Phase 1 (prep) migration plan for swc-infield-button: 1st-gen
API surface, breaking changes, 2nd-gen API decisions, core/SWC split,
and the full Setup/API/Styling/A11y/Testing/Docs checklist.

Design and CSS inputs are confirmed against the Figma S2 In-field button
spec and the spectrum-two infieldbutton/index.css baseline. Styling and
section structure follow the migration-prep template and align with the
button/action-button plans. Updates the components README nav.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Rajdeepc Rajdeepc requested a review from a team as a code owner June 23, 2026 11:37
@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 164bd13

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@Rajdeepc Rajdeepc self-assigned this Jun 23, 2026
@coveralls

coveralls commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 28187632272

Warning

No base build found for commit 9a9ddbe on main.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 96.244%

Details

  • Patch coverage: No coverable lines changed in this PR.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 39173
Covered Lines: 37902
Line Coverage: 96.76%
Relevant Branches: 6459
Covered Branches: 6016
Branch Coverage: 93.14%
Branches in Coverage %: Yes
Coverage Strength: 458.85 hits per line

💛 - Coveralls

@Rajdeepc Rajdeepc added Status:Ready for review PR ready for review or re-review. Spectrum 2 Issues related to Spectrum 2 run_vrt Triggers the Chromatic VRT run for 2nd-gen and removed run_vrt Triggers the Chromatic VRT run for 2nd-gen labels Jun 23, 2026

| # | What changes | 1st-gen behavior | 2nd-gen behavior | Consumer migration path |
| --- | ------------ | ---------------- | ---------------- | ----------------------- |
| **B1** | Remove `block` attribute | `block="start"/"end"` adjusted corner radius and border for stacked stepper pairs | Removed. S2 uses consistent corner radius; parent field host owns stacked layout via DOM order and CSS | Remove `block` attribute from all `sp-infield-button` usages. Restructure parent field layout to manage corner radius and borders on the containing field element. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We could clean this up a bit, it's a bit misleading. S2 won't have a stacked layout as the buttons will lay side by side, I believe. And because the buttons aren't flush against the parent field anymore, we don't need to manage corner radius and borders as before.

Suggested change
| **B1** | Remove `block` attribute | `block="start"/"end"` adjusted corner radius and border for stacked stepper pairs | Removed. S2 uses consistent corner radius; parent field host owns stacked layout via DOM order and CSS | Remove `block` attribute from all `sp-infield-button` usages. Restructure parent field layout to manage corner radius and borders on the containing field element. |
| **B1** | Remove `block` attribute | `block="start"/"end"` adjusted corner radius and border for stacked stepper pairs | Removed. S2 uses consistent corner radius. | Remove `block` attribute from all `sp-infield-button` usages. |

| --- | ------------ | ---------------- | ---------------- | ----------------------- |
| **B1** | Remove `block` attribute | `block="start"/"end"` adjusted corner radius and border for stacked stepper pairs | Removed. S2 uses consistent corner radius; parent field host owns stacked layout via DOM order and CSS | Remove `block` attribute from all `sp-infield-button` usages. Restructure parent field layout to manage corner radius and borders on the containing field element. |
| **B2** | Remove `inline` attribute | `inline="start"/"end"` adjusted corner radius and edge attachment for horizontal inline groups | Removed. Same reason as B1. The parent field host owns inline-group layout (it renders the `.swc-InfieldButton-inline` wrapper). | Remove `inline` attribute. Ensure the parent field host wraps the in-field buttons in the `.swc-InfieldButton-inline` container (or equivalent CSS context). |
| **B3** | Rename `label` → `accessible-label` | `label` attribute maps to `aria-label` on inner `<button>` | `accessible-label` attribute maps to `aria-label` on inner `<button>`, via `ButtonBase`'s `accessibleLabel` property | Replace `label="…"` with `accessible-label="…"` on each `sp-infield-button`. **Required for icon-only; dev warning fires if omitted.** |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

"Required for icon-only" is a bit confusing here given that the component is always icon-only.

| **B2** | Remove `inline` attribute | `inline="start"/"end"` adjusted corner radius and edge attachment for horizontal inline groups | Removed. Same reason as B1. The parent field host owns inline-group layout (it renders the `.swc-InfieldButton-inline` wrapper). | Remove `inline` attribute. Ensure the parent field host wraps the in-field buttons in the `.swc-InfieldButton-inline` container (or equivalent CSS context). |
| **B3** | Rename `label` → `accessible-label` | `label` attribute maps to `aria-label` on inner `<button>` | `accessible-label` attribute maps to `aria-label` on inner `<button>`, via `ButtonBase`'s `accessibleLabel` property | Replace `label="…"` with `accessible-label="…"` on each `sp-infield-button`. **Required for icon-only; dev warning fires if omitted.** |
| **B4** | Remove link API (`href`, `target`, `download`, `rel`, `referrerpolicy`) | Inherited from 1st-gen `ButtonBase` via `LikeAnchor` mixin | Not implemented. `swc-infield-button` is always `type="button"`. 2nd-gen `ButtonBase` does not expose link attributes. | Remove any `href`-based usage of `sp-infield-button`. Use `<a>` or `swc-button` with an `href` when navigation is needed in a field context (uncommon). |
| **B5** | Remove `--mod-infield-button-*` CSS custom properties | 17 modifier properties supported | None of the `--mod-infield-button-*` properties are carried forward. `--swc-infield-button-*` properties may be introduced selectively (see A1). | Consumers overriding infield-button styling via `--mod-infield-button-*` must switch to `--swc-infield-button-*` equivalents when available, or use CSS custom property targeting directly. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not totally sure what "use CSS custom property targeting directly" means, but overall our styling in Gen 2 is meant to be less reliant on custom properties and would use the tokens directly when appropriate.

| **B3** | Rename `label` → `accessible-label` | `label` attribute maps to `aria-label` on inner `<button>` | `accessible-label` attribute maps to `aria-label` on inner `<button>`, via `ButtonBase`'s `accessibleLabel` property | Replace `label="…"` with `accessible-label="…"` on each `sp-infield-button`. **Required for icon-only; dev warning fires if omitted.** |
| **B4** | Remove link API (`href`, `target`, `download`, `rel`, `referrerpolicy`) | Inherited from 1st-gen `ButtonBase` via `LikeAnchor` mixin | Not implemented. `swc-infield-button` is always `type="button"`. 2nd-gen `ButtonBase` does not expose link attributes. | Remove any `href`-based usage of `sp-infield-button`. Use `<a>` or `swc-button` with an `href` when navigation is needed in a field context (uncommon). |
| **B5** | Remove `--mod-infield-button-*` CSS custom properties | 17 modifier properties supported | None of the `--mod-infield-button-*` properties are carried forward. `--swc-infield-button-*` properties may be introduced selectively (see A1). | Consumers overriding infield-button styling via `--mod-infield-button-*` must switch to `--swc-infield-button-*` equivalents when available, or use CSS custom property targeting directly. |
| **B6** | Icons move from default slot → `icon` slot | `<sp-infield-button label="…"><sp-icon-add></sp-icon-add></sp-infield-button>` (icon in default slot) | Icon must be in the named `icon` slot to trigger `ButtonBase`'s `hasIcon` detection and the `accessible-label` dev warning | Add `slot="icon"` to slotted icon elements: `<swc-infield-button accessible-label="…"><sp-icon-add slot="icon"></sp-icon-add></swc-infield-button>` |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This <swc-infield-button...> code snippet includes <sp-icon>, should it be <swc-icon> instead?

I think if #6415 goes through we'd also be slotting the <svg> element into <swc-icon> rather than using something like <swc-icon-add>


| Slot | Content | Notes |
| ---- | ------- | ----- |
| `icon` | Icon element | **Confirmed.** Named `icon` slot (from `ButtonBase`). `ObserveSlotPresence` in `ButtonBase` monitors `[slot="icon"]` for icon presence. Icon must be decorative (`aria-hidden="true"` on the icon element when `accessible-label` provides the name). **Breaking change from 1st-gen default slot (B6).** |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I read this part about the aria-hidden and interpret it as being something that needs to be added manually, I think the icon component takes care of putting aria-hidden="true" on the icon element so it's not necessary to add it, but worth double-checking.

That might just be me misreading what this is trying to say, definitely feel free to disregard this comment if you don't think it needs a change.

- Disabled: background `token("disabled-background-color")`, icon `token("disabled-content-color")`
- Quiet: `transparent` background for default, hover, active, and disabled (not the gray disabled color)
- [ ] Focus model: the inner `<button>` sets `outline: none` (matches S2 source). In **composed** field contexts the parent field (number-field, textfield, picker) owns the focus-visible ring while the inner `<button>` stays keyboard-reachable via `delegatesFocus`; verify with a field-level story. In **isolated** standalone usage there is no visible focus indicator by default; if one is required it must be added deliberately and meet WCAG 2.4.7. Do not add a competing `:focus-visible` outline that would double-ring inside a field
- [ ] Forced colors (Windows High Contrast): default variant on `.swc-InfieldButton` → background `ButtonText`, icon `ButtonFace`, `forced-color-adjust: none` on the `<button>`; disabled → background `GrayText`; hover/active/focus-visible → background `Highlight`. Quiet variant → background `Canvas`, icon `ButtonText`; disabled → `Canvas` / `GrayText`; hover/active/focus-visible → `Canvas` / `Highlight`. Sort the `@media (forced-colors: active)` block to the bottom of the file per style guide

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I have some doubts about this item. Our style guide explicitly says:

Forced colors styles are only included when browser defaults are not sufficient

So I would hesitate to just bring over styles from spectrum-css before checking them manually.

However, after consulting with a couple of agents, they both pushed back on my doubts and said it was reasonable to port the styles over from spectrum-css... wonder if we need an adjustment in a skill or doc somewhere? Tagging @5t3ph if she has an opinion on this.


| # | Item | Blocking? | Status | Owner |
| --- | ---- | --------- | ------ | ----- |
| **Q3** | **Migration ordering: infield-button before number-field.** Recommended based on the number-field rendering analysis explicitly composing `swc-infield-button`. If the team has a different sequencing preference, both this plan and the number-field analysis need updating. | Yes — affects planning | Open (recommendation made) | Architecture reviewer. Next action: confirm or override the sequencing recommendation. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We might be able to remove this item or at least close it out - the sequencing seems to be confirmed, especially given that we're working on this migration plan first and holding number field for later.

| --- | ---- | --------- | ------ | ----- |
| **Q3** | **Migration ordering: infield-button before number-field.** Recommended based on the number-field rendering analysis explicitly composing `swc-infield-button`. If the team has a different sequencing preference, both this plan and the number-field analysis need updating. | Yes — affects planning | Open (recommendation made) | Architecture reviewer. Next action: confirm or override the sequencing recommendation. |
| **Q4** | **Component existence: should `swc-infield-button` remain a standalone component?** The ticket explicitly asks whether this component is needed and whether it intersects with Clear or Close button. **Recommendation: keep as a standalone component.** Rationale: (1) The CSS tokens are specialized (`--spectrum-in-field-button-*`) and designed for field-chrome integration. (2) The quiet variant has unique transparent-background field-integration behavior. (3) Neither `swc-clear-button` nor `swc-close-button` has a migration analysis yet — merging before either is analyzed would be premature. (4) The number-field migration explicitly depends on `swc-infield-button`. (5) There is no React Spectrum counterpart to reconcile with. **Counter-argument:** If the team decides `swc-clear-button` and quiet `swc-infield-button` are functionally equivalent for the clear-field use case, they should be consolidated before either migrates to avoid a second breaking change. This is Q5 below. | Yes — decision affects scope | Open (recommendation made) | Architecture reviewer. Next action: team confirms keep vs consolidate before implementation begins. |
| **Q5** | **Scope intersection with `swc-clear-button`: who owns "clear field" affordances?** In 1st-gen, `sp-infield-button` is used for generic field actions (stepper +/−, disclosure). `sp-clear-button` is a separate component. In 2nd-gen, if `swc-infield-button` (quiet variant) and `swc-clear-button` have the same visual appearance, there is an argument for a single component. **Recommendation: keep separate, but resolve the composition contract before `swc-number-field` migration.** Confirm: does `swc-textfield` / `swc-search` use `swc-infield-button` or `swc-clear-button` for their clear actions? The answer affects the scope boundary and the consumer migration guide for both components. The Figma In-field button spec ([`126176-34080`](https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Web--Desktop-scale-?node-id=126176-34080)) documents a **clear** icon affordance as one of the in-field button icon types, which is direct evidence for the overlap. Next action: review that frame and the textfield/search rendering analyses to confirm which button type each field uses for clear. | Partial — blocks consumer guide scope | Open | Architecture + design. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Next action: review that frame and the textfield/search rendering analyses to confirm which button type each field uses for clear.

These exist, no? Feels like we could probably have an agent knock this out quickly and close this out? Also I believe textfield/search and also tag use the clear-button rather than the infield button.

@github-actions

Copy link
Copy Markdown
Contributor

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6441

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

Rajdeep Chandra and others added 2 commits June 25, 2026 17:11
The Sidenav accessibility-migration-analysis entry in preview.ts belongs
to a separate commit and should not be part of this migration plan PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@5t3ph 5t3ph left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like the agent was mixing up ButtonBase from 1st-gen vs 2nd-gen, and I probably missed some spots where that impacts the recommendations here.

| --- | ------------ | ---------------- | ---------------- | ----------------------- |
| **S1** | CSS custom properties (see B5 in API table above) | `--mod-infield-button-*` | `--swc-infield-button-*` (limited set; see A1) | Same as B5 in API table |
| **S2** | Stacked-border tokens removed | `--mod-infield-button-stacked-*` tokens handled inner border radius resets for stacked pairs | Removed entirely; S2 uses a consistent corner radius on each button | No consumer action needed if `block`/`inline` are removed per B1/B2 |
| **S3** | Focus state ownership shifts to parent | 1st-gen: each `sp-infield-button` manages its own `:focus-visible` ring | 2nd-gen: the inner `<button>` suppresses its own outline (`outline: none`, matching S2 source); the parent field shows the focus ring in composed contexts while the inner `<button>` stays keyboard-reachable | Verify with field-level stories that focus visibility is preserved and meets WCAG 2.4.7 |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the button is independently focusable, then it needs to retain a focus ring for keyboard a11y.

| `size` | `'s' \| 'm' \| 'l' \| 'xl'` | none | `size` | **Confirmed** | `noDefaultSize: true` preserved; parent field host must provide size. |
| `disabled` | `boolean` | `false` | `disabled` | **Confirmed** | From `ButtonBase`. Both self-disabled and parent-driven `disabled` must work. |
| `accessibleLabel` | `string \| undefined` | undefined | `accessible-label` | **Confirmed** | From 2nd-gen `ButtonBase`. Required for icon-only. Dev warning fires when `hasIcon && !hasLabel && !accessibleLabel`. |
| `active` | `boolean` | `false` | `active` | **Inferred** | From `ButtonBase`. Preserves pressed visual state. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not available in 2nd-gen ButtonBase. Do we actually need this state? What is the use case for a "toggle" button in-field?

| `disabled` | `boolean` | `false` | `disabled` | **Confirmed** | From `ButtonBase`. Both self-disabled and parent-driven `disabled` must work. |
| `accessibleLabel` | `string \| undefined` | undefined | `accessible-label` | **Confirmed** | From 2nd-gen `ButtonBase`. Required for icon-only. Dev warning fires when `hasIcon && !hasLabel && !accessibleLabel`. |
| `active` | `boolean` | `false` | `active` | **Inferred** | From `ButtonBase`. Preserves pressed visual state. |
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | `type` | **Confirmed** | From `ButtonBase`. Always `"button"` for in-field adornments in practice; `submit`/`reset` deferred (A3). |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also not in 2nd-gen ButtonBase, so perhaps consider it dropped from the API or at least deferred?

Additional presentation modes **not supported in 2nd-gen:**
- Stacked position variant (`block="start"/"end"`) — removed; consistent corner radius in S2
- Inline position variant (`inline="start"/"end"`) — removed; the parent field host owns inline-group layout (it renders the `.swc-InfieldButton-inline` wrapper around the slotted buttons), not `swc-infield-button` itself
- Static color (`static-color="white"/"black"`) — **not present** in Figma S2 spec; confirmed absent (not in 1st-gen; no field-chrome use case for static color)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ButtonBase is currently slated to get staticColor exposed via this PR, so if that happens you'll need to exclude it.

Comment on lines +294 to +295
| `icon` | Icon element | **Confirmed.** Named `icon` slot (from `ButtonBase`). `ObserveSlotPresence` in `ButtonBase` monitors `[slot="icon"]` for icon presence. The `swc-icon` component sets `aria-hidden="true"` on itself; no manual attribute needed on the slotted element. **Breaking change from 1st-gen default slot (B6).** |
| (default) | Optional visible label text | **Inferred.** Unlikely to contain content in typical use — `swc-infield-button` is always icon-only in practice. Present because `ButtonBase` exposes it; `ObserveSlotText` monitors it for `hasLabel`. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These are not in 2nd-gen ButtonBase since that doesn't manage render.

Comment on lines +363 to +364
[`swc-InfieldButton--size${size?.toUpperCase()}`]: !!size,
'swc-InfieldButton--quiet': this.quiet,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[nit] attribute selectors will be used for these states

'swc-InfieldButton--quiet': this.quiet,
})}
>
<div class="swc-InfieldButton-fill">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[nit] this is kind of odd naming, what is "fill" trying to communicate? Also I wonder if this will be able to be removed in favor of just ::slotted() styles.

Rajdeep Chandra and others added 2 commits June 25, 2026 22:43
- S3: clarify that standalone use retains its own focus-visible ring; the
  outline:none suppression applies only in composed field contexts
- Drop 'active' from public API: not in ButtonBase; :active CSS handles
  pressed visual; no toggle use case in-field
- Drop 'type' from public API: not in ButtonBase; concrete template hardcodes
  type="button"; submit/reset deferred to A3
- static-color: add note that if ButtonBase gains staticColor (PR #6410),
  swc-infield-button must explicitly exclude it
- Default slot: fix wording — slots are in the concrete template, not
  provided by ButtonBase (which has no render method)
- Render template: replace BEM modifier classMap with attribute selectors;
  quiet and size are reflected so :host([quiet]) / :host([size="s"]) apply
- Remove fill wrapper div from template; add Phase 5 note to verify whether
  the wrapper is required or ::slotted() alone is sufficient

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Rajdeepc Rajdeepc force-pushed the rajdeepc/docs-infield-button-migration-plan branch from 7c9d325 to 164bd13 Compare June 25, 2026 17:13
@Rajdeepc Rajdeepc requested review from 5t3ph and rise-erpelding June 25, 2026 17:16
- Active/down: background `token("gray-200")`, icon `token("neutral-content-color-down")`; `transform: perspective(...) translateZ(...)` on the `<button>` using the S2 downstate tokens
- Disabled: background `token("disabled-background-color")`, icon `token("disabled-content-color")`
- Quiet: `transparent` background for default, hover, active, and disabled (not the gray disabled color)
- [ ] Focus model: the inner `<button>` sets `outline: none` (matches S2 source). In **composed** field contexts the parent field (number-field, textfield, picker) owns the focus-visible ring while the inner `<button>` stays keyboard-reachable via `delegatesFocus`; verify with a field-level story. In **isolated** standalone usage there is no visible focus indicator by default; if one is required it must be added deliberately and meet WCAG 2.4.7. Do not add a competing `:focus-visible` outline that would double-ring inside a field

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Further up, the plan says:

When used standalone (outside a parent field), the button must retain its own :focus-visible ring to satisfy WCAG 2.4.7.

Here it says:

In isolated standalone usage there is no visible focus indicator by default; if one is required it must be added deliberately and meet WCAG 2.4.7.

We might change this part to remove the "if one is required"


- [ ] `InfieldButton.types.ts`: define `InfieldButtonSize` (alias or reuse from `BUTTON_VALID_SIZES`), `InfieldButtonQuiet` boolean
- [ ] `InfieldButton.base.ts`: extend 2nd-gen `ButtonBase` with `SizedMixin(ButtonBase, { noDefaultSize: true })`; `validSizes` does not need overriding because `BUTTON_VALID_SIZES` already equals `['s', 'm', 'l', 'xl']` (see Q6); add `quiet: boolean` property (reflect: true)
- [ ] `InfieldButton.ts`: extend `InfieldButton.base.ts`; register as `swc-infield-button`; render inner `<button>` (with size and `quiet` modifier classes via `classMap`) containing a `.swc-InfieldButton-fill` div that wraps the `icon` slot

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We're not using classMap for size/quiet modifiers, we'd use the host attribute selectors - this appears in a couple places:

Here:

(with size and quiet modifier classes via classMap)

Around L429:

Apply .swc-InfieldButton (with size/quiet modifier classes via classMap)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also, an additional note that we're saying earlier that the fill <div> might not be needed, it could be helpful for the agent to ensure that's mentioned again here

- [ ] Apply `.swc-InfieldButton` (with size/quiet modifier classes via `classMap`) to the inner `<button>`; keep styling off `:host`
- [ ] Update class and custom property prefixes from `.spectrum-InfieldButton` to `.swc-InfieldButton`; **remove all `--mod-infield-button-*` and `--spectrum-infield-button-*` fallback chains**, collapsing each into a single intentional `--swc-infield-button-*` property with a `token(...)` default per the [custom properties style guide](../../../../CONTRIBUTOR-DOCS/02_style-guide/01_css/02_custom-properties.md)
- [ ] Style the slotted icon with `slot[name="icon"]::slotted(*)` (color + size-specific padding), as `swc-button` does; do **not** put a `.swc-InfieldButton-icon` class on the consumer-slotted node (the S2 `.spectrum-InfieldButton-icon` rule targets an inline SVG in the CSS-only template, which does not apply to the slotted web-component case)
- [ ] `.swc-InfieldButton-fill` (inner `<div>`) owns `background-color`, `border-radius`, and the centering flex

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Noting we said earlier that .swc-InfieldButton-fill might not be necessary, I mentioned this as well in my comment on L412

`swc-infield-button` has no `href` or related anchor attributes. It is always `type="button"` in practice.

**`noDefaultSize: true`:**
The component does not pick a default size. The parent field host must set `size` (either by attribute or inherited context). This behavior is preserved from 1st-gen. 2nd-gen `ButtonBase` already applies `SizedMixin` with `validSizes: BUTTON_VALID_SIZES` (= `['s', 'm', 'l', 'xl']`), so `InfieldButton.base.ts` only needs to re-apply `SizedMixin(..., { noDefaultSize: true })`; the `validSizes` set already matches and does not need to be overridden (see Q6). Note that M is the Figma **reference** size, not a component default.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Claude's informed me that we might copy the action button's approach on this?

Additional presentation modes **not supported in 2nd-gen:**
- Stacked position variant (`block="start"/"end"`) — removed; consistent corner radius in S2
- Inline position variant (`inline="start"/"end"`) — removed; the parent field host owns inline-group layout (it renders the `.swc-InfieldButton-inline` wrapper around the slotted buttons), not `swc-infield-button` itself
- Static color (`static-color="white"/"black"`) — **not present** in Figma S2 spec; confirmed absent (not in 1st-gen; no field-chrome use case for static color). **Note:** if `ButtonBase` gains a `staticColor` property (tracked in PR #6410), `swc-infield-button` must explicitly exclude or override it to prevent consumers from setting an unsupported value.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we also need to do this for pending? The infield button doesn't have pending, but the button base does, so we would need to ensure it doesn't unintentionally set pending styles without any pending action?

@5t3ph 5t3ph left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Apologies for causing confusion on the focus behavior. I have chatted with Nikki and gotten clarification on the expected behavior.

Infield button focus/keyboard behavior corrections

  • Infield buttons are never standalone — remove that scenario and its :focus-visible ring requirement.
  • They are clickable but not focusable. Keyboard behavior equivalent to clicking (e.g., arrow keys for increment/decrement on number field) is owned by the parent field, not the button.
  • The parent field's focus ring is the only focus indicator needed — no separate WCAG 2.4.7 concern for the button itself.

Please update the plan to reflect this with language that makes the non-focusable, parent-delegated keyboard model explicit.

- Stacked position variant (`block="start"/"end"`) — removed; consistent corner radius in S2
- Inline position variant (`inline="start"/"end"`) — removed; the parent field host owns inline-group layout (it renders the `.swc-InfieldButton-inline` wrapper around the slotted buttons), not `swc-infield-button` itself
- Static color (`static-color="white"/"black"`) — **not present** in Figma S2 spec; confirmed absent (not in 1st-gen; no field-chrome use case for static color)
- Static color (`static-color="white"/"black"`) — **not present** in Figma S2 spec; confirmed absent (not in 1st-gen; no field-chrome use case for static color). **Note:** if `ButtonBase` gains a `staticColor` property (tracked in PR #6410), `swc-infield-button` must explicitly exclude or override it to prevent consumers from setting an unsupported value.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Update that the close button PR ended up reverting this decision since at least infield and clear do not use static colors

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Spectrum 2 Issues related to Spectrum 2 Status:Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants