feat(Switch)!: Add a built-in label, placeable on either side - #2939
feat(Switch)!: Add a built-in label, placeable on either side#2939RubenSibon wants to merge 5 commits into
Conversation
…t/DES-1917-switch-with-label-redesign
Coverage Report for React components
File Coverage
|
||||||||||||||||||||||||||||||||||||||
|
Size Change: +320 B (+0.08%) Total Size: 426 kB 📦 View Changed
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Pull request overview
Adds a built-in, positionable label to Switch across tokens, CSS, React, tests, and documentation.
Changes:
- Adds label children and
labelPosition. - Introduces track-specific markup, styling, and tokens.
- Updates stories, documentation, and accessibility coverage.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
storybook/src/modes/lo-fi/Forms.test.stories.tsx |
Labels the lo-fi Switch. |
storybook/src/components/Switch/Switch.test.stories.tsx |
Updates visual variants. |
storybook/src/components/Switch/Switch.stories.tsx |
Documents label controls and placement. |
storybook/src/components/Switch/Switch.docs.mdx |
Explains the new label behavior. |
packages/react/src/Switch/Switch.tsx |
Implements built-in label markup. |
packages/react/src/Switch/Switch.test.tsx |
Tests labeling and placement. |
packages/css/src/components/switch/switch.scss |
Styles labels and track structure. |
packages-proprietary/tokens/src/components/ams/switch.tokens.json |
Adds label and track tokens and aliases. |
Suppressed comments (7)
packages-proprietary/tokens/src/components/ams/switch.tokens.json:135
ams.switch.track-container.block-sizeis consumed by theblock-sizelonghand, so this new dimension token needs the mandatory single-value hint frompackages-proprietary/tokens/AGENTS.md:45-55; otherwise a two-value theme override invalidates the declaration.
"$extensions": {
"nl.amsterdam.type": "dimension"
}
packages-proprietary/tokens/src/components/ams/switch.tokens.json:81
- This new dimension token is assigned to the
text-underline-offsetlonghand but lacks the required single-value hint. Without that metadata, a theme can supply two values and make the declaration invalid at computed-value time. Add the longhand hint required bypackages-proprietary/tokens/AGENTS.md:45-55.
"$extensions": {
"nl.amsterdam.subtype": "space",
"nl.amsterdam.type": "dimension"
}
packages-proprietary/tokens/src/components/ams/switch.tokens.json:109
ams.switch.track.inline-sizeis consumed by theinline-sizelonghand, so this new dimension token also needs the mandatory single-value hint frompackages-proprietary/tokens/AGENTS.md:45-55; otherwise a two-value theme override invalidates the declaration.
"$type": "dimension"
packages-proprietary/tokens/src/components/ams/switch.tokens.json:50
- This alias repeats a removal date that is less than the required six months away. As of 2026-08-18 the date is only 179 days out, before accounting for merge and release time;
packages-proprietary/tokens/README.md:278-280requires at least six months. Move it to at least six months after the expected release.
"$deprecated": "Use `ams.switch.track.inline-size` instead. Will be removed on or after 2027-02-13.",
packages-proprietary/tokens/src/components/ams/switch.tokens.json:192
- This alias repeats a removal date that is less than the required six months away. As of 2026-08-18 the date is only 179 days out, before accounting for merge and release time;
packages-proprietary/tokens/README.md:278-280requires at least six months. Move it to at least six months after the expected release.
"$deprecated": "Use `ams.switch.track.checked.background-color` instead. Will be removed on or after 2027-02-13.",
packages-proprietary/tokens/src/components/ams/switch.tokens.json:85
- This alias repeats a removal date that is less than the required six months away. As of 2026-08-18 the date is only 179 days out, before accounting for merge and release time;
packages-proprietary/tokens/README.md:278-280requires at least six months. Move it to at least six months after the expected release.
"$deprecated": "Use `ams.switch.track.border-width` instead. Will be removed on or after 2027-02-13.",
packages-proprietary/tokens/src/components/ams/switch.tokens.json:201
- This alias repeats a removal date that is less than the required six months away. As of 2026-08-18 the date is only 179 days out, before accounting for merge and release time;
packages-proprietary/tokens/README.md:278-280requires at least six months. Move it to at least six months after the expected release.
"$deprecated": "Use `ams.switch.track.disabled.background-color` instead. Will be removed on or after 2027-02-13.",
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "background-color": { | ||
| "$value": "#767676", | ||
| "$type": "color" | ||
| "$deprecated": "Use `ams.switch.track.background-color` instead. Will be removed on or after 2027-02-13.", |
There was a problem hiding this comment.
Should be addressed if we move forward with this.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
feat(Switch)!: Add a built-in label, placeable on either side
Note
This draft PR represents research for a possible new visual design for Switch. It is by no means final.
Links
What
children, wrapping the track and the text in onelabelelement the way Checkbox and Radio do.labelPosition?: 'start' | 'end'prop (defaultend) puts the label on either side of the track..ams-switch__labelinto its own.ams-switch__track, inside a.ams-switch__track-containersized to one line of text.ams.switch.track.*; the old names stay as deprecated aliases.WithLabelstory and its wrappingdivwith a hardcoded5remgap are gone, replaced byLabelBeforeTheSwitch.Breaking:
.ams-switch__labelused to be the track and is now the label wrapper. Anyone hooking into that class outside the React component gets label styling where they expected a track. Design tokens and CSS custom properties are not breaking — every renamed one keeps working through a deprecated alias.Why
A Switch had no label of its own, so it had to borrow the
Labelcomponent throughhtmlFor.Labelis block-level and bold — right above a text input, too heavy beside a switch. It also left the spacing to the consumer, which is why the story needed a flex wrapper with a5remgap to look presentable.Checkbox and Radio already solve this: the label element holds both the control's visual and its text, in body copy. Switch now matches them, so the three read as one family in a form and a Switch no longer needs any external layout to be usable.
How
SwitchPropsnow renderschildren(it accepted them viaPropsWithChildrenbut silently dropped them) and omitstypefrom the inherited input attributes, matchingCheckboxPropsandRadioProps.labelPositionreorders the DOM rather than reversing with CSS, so reading order always matches visual order and no modifier class is needed..ams-switch__labelpicks up the Checkbox/Radio typography set: font, gap, hover underline, disabled colour,hyphenationandtext-rendering..ams-switch__track-containeris one line of text tall withalign-items: center, mirroring.ams-checkbox__icon-container, so the track centres on the first line and stays there when a long label wraps..ams-switch:hoverto.ams-switch__label:hover, now that the block contains text.$deprecatedaliases forwarding to the new names (removable on or after 2027-02-13), read in SCSS asvar(--old /* @deprecated */, var(--new)).disablePageLevelChecks('label')— like Checkbox and Radio, its variants now have accessible names — and gained thedisabledvariant.Checklist
Before submitting your pull request, please ensure you have done the following. Check each checkmark if you have done so or if it wasn't necessary:
/chromatic testand verify visual regression tests passAdditional notes
switchLabelPositionsis exported fromSwitch.tsxbut deliberately not from the barrel, followingrowGapSizesandrowTags. Noindex.*changes were needed.disabledvariant was added to the test story.2remtall against a line box of1.8remat narrow viewports (they match exactly at 1440px), so it overflows its container by roughly 1.6px top and bottom. I chose that over shrinking the track to keep the baseline aligned with a Checkbox in the same Column — worth a design opinion.Switchwith no label at all; it now has one.pnpm run lintandpnpm run testboth pass locally.