Skip to content

feat(Switch)!: Add a built-in label, placeable on either side - #2939

Draft
RubenSibon wants to merge 5 commits into
developfrom
feat/DES-1917-switch-with-label-redesign
Draft

feat(Switch)!: Add a built-in label, placeable on either side#2939
RubenSibon wants to merge 5 commits into
developfrom
feat/DES-1917-switch-with-label-redesign

Conversation

@RubenSibon

@RubenSibon RubenSibon commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

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

  • Switch renders its own label from children, wrapping the track and the text in one label element the way Checkbox and Radio do.
  • New labelPosition?: 'start' | 'end' prop (default end) puts the label on either side of the track.
  • The track moves out of .ams-switch__label into its own .ams-switch__track, inside a .ams-switch__track-container sized to one line of text.
  • Track-related design tokens move under ams.switch.track.*; the old names stay as deprecated aliases.
  • The WithLabel story and its wrapping div with a hardcoded 5rem gap are gone, replaced by LabelBeforeTheSwitch.

Breaking: .ams-switch__label used 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 Label component through htmlFor. Label is 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 a 5rem gap 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

  • SwitchProps now renders children (it accepted them via PropsWithChildren but silently dropped them) and omits type from the inherited input attributes, matching CheckboxProps and RadioProps.
  • labelPosition reorders the DOM rather than reversing with CSS, so reading order always matches visual order and no modifier class is needed.
  • .ams-switch__label picks up the Checkbox/Radio typography set: font, gap, hover underline, disabled colour, hyphenation and text-rendering.
  • .ams-switch__track-container is one line of text tall with align-items: center, mirroring .ams-checkbox__icon-container, so the track centres on the first line and stays there when a long label wraps.
  • Hover moved from .ams-switch:hover to .ams-switch__label:hover, now that the block contains text.
  • Renamed tokens keep the established deprecation pattern: $deprecated aliases forwarding to the new names (removable on or after 2027-02-13), read in SCSS as var(--old /* @deprecated */, var(--new)).
  • The test story dropped disablePageLevelChecks('label') — like Checkbox and Radio, its variants now have accessible names — and gained the disabled variant.

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:

  • Add or update unit tests
  • Add or update documentation
  • Add or update stories
  • Add or update exports in index.* files
  • Comment /chromatic test and verify visual regression tests pass
  • Start the PR title with a Conventional Commit prefix, as explained here.

Additional notes

  • switchLabelPositions is exported from Switch.tsx but deliberately not from the barrel, following rowGapSizes and rowTags. No index.* changes were needed.
  • Chromatic will show diffs for every Switch snapshot: the labels are new, and the disabled variant was added to the test story.
  • The track is 2rem tall against a line box of 1.8rem at 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.
  • The lo-fi Forms test story had a Switch with no label at all; it now has one.
  • pnpm run lint and pnpm run test both pass locally.

@RubenSibon RubenSibon self-assigned this Aug 18, 2026
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for React components

Status Category Percentage Covered / Total
🟢 Lines 100% 1160 / 1160
🟢 Statements 100% 1233 / 1233
🟢 Functions 100% 310 / 310
🟢 Branches 100% 910 / 910
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/Switch/Switch.tsx 100% 100% 100% 100%
Generated in workflow #488 for commit 22e907f by the Vitest Coverage Report Action

@RubenSibon
RubenSibon requested a balanced review from Copilot August 18, 2026 12:15
@RubenSibon RubenSibon changed the title Feat/des 1917 switch with label redesign feat(Switch)!: Add a built-in label, placeable on either side Aug 18, 2026
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Size Change: +320 B (+0.08%)

Total Size: 426 kB

📦 View Changed
Filename Size Change
packages-proprietary/tokens/dist/index.css 11.4 kB +167 B (+1.49%)
packages/css/dist/index.css 18.1 kB +117 B (+0.65%)
packages/react/dist/index.esm.js 50.2 kB +36 B (+0.07%)
ℹ️ View Unchanged
Filename Size
packages-proprietary/react-icons/dist/index.esm.js 55.3 kB
packages-proprietary/temp/assets-fonts.tar.gz 168 kB
packages-proprietary/temp/assets-others.tar.gz 120 kB
packages-proprietary/tokens/dist/compact.css 600 B
packages-proprietary/tokens/dist/lo-fi.css 1.56 kB
packages/react/dist/index.js 492 B

compressed-size-action

Copilot AI 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.

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-size is consumed by the block-size longhand, so this new dimension token needs the mandatory single-value hint from packages-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-offset longhand 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 by packages-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-size is consumed by the inline-size longhand, so this new dimension token also needs the mandatory single-value hint from packages-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-280 requires 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-280 requires 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-280 requires 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-280 requires 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.

Comment thread packages-proprietary/tokens/src/components/ams/switch.tokens.json
"background-color": {
"$value": "#767676",
"$type": "color"
"$deprecated": "Use `ams.switch.track.background-color` instead. Will be removed on or after 2027-02-13.",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should be addressed if we move forward with this.

Comment thread storybook/src/components/Switch/Switch.docs.mdx Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@github-actions
github-actions Bot temporarily deployed to demo-DES-1917-switch-with-label-redesign August 18, 2026 12:34 Destroyed
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants