feat(components): add labelPosition to Switch - #1996
Conversation
Co-Authored-By: Harsh Sadhvani <hsadhvani@launchdarkly.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
🦋 Changeset detectedLatest commit: 2fa9404 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
|
@hsadhvani I was wondering if we could we support this through composition rather than adding a |
|
@erangeles good call — composition is doable, and upstream is already heading there. RAC 1.19 (the version we're on) deprecates <SwitchField>
<Label>AI assist</Label>
<SwitchButton />
</SwitchField>// react-aria-components 1.19.0, dist/types/src/Switch.d.ts
/** @deprecated Use SwitchField + SwitchButton instead. */
export declare const Switch: ...Doing this properly in Launchpad means a new Composition within today's So: do you want me to (a) land Context: this is unblocking a Gonfalon experiment form where the "AI assist" label should read left of its toggle (launchdarkly/gonfalon#69412), currently shipping a local |
Co-Authored-By: Harsh Sadhvani <hsadhvani@launchdarkly.com>
Co-Authored-By: Harsh Sadhvani <hsadhvani@launchdarkly.com>
Runtime verification —
|
Default (WithChildren) |
New (LabelStart) |
|---|---|
![]() |
![]() |
Both start at the same left edge of the canvas — full window: LabelStart / WithChildren.
Clicking the label text still toggles (the point of keeping the label as children): green track, On moves to the track's left, handle animates right, label stays before the track.
Compact variant (switchLabels={false} + labelPosition="start")
Added as a permanent LabelStartHideLabels story.
| Unselected | Selected |
|---|---|
![]() |
![]() |
Regressions checked
WithChildren selected state and the existing Hide Labels compact story render as before.
Not covered: flipping labelPosition live via the Controls panel (URL args were inert in this Storybook build); the per-story comparison covers the same behavior.





Summary
Switchrenders[track][children], so a visible label always sits after the toggle and there's no way to put the label first without leaving the switch. Consumers that want label-before-toggle currently either overrideflex-directionlocally (see launchdarkly/gonfalon#69412) or render the text as a sibling element witharia-labelon the switch — the latter drops the text out of the<label>, so clicking the words no longer toggles and the visible text is no longer the accessible name.This adds a
labelPositionvariant, defaulting toend(current behavior):The label stays
childrenof the switch, so it remains inside the<label>— clickable and still the accessible name — and only the visual order changes. Track internals (On/Off labels, handle translation, compact) are untouched.Naming: RAC's
Switchhas no equivalent prop, so this follows React Spectrum'slabelPositionname with CSS-logicalstart/endvalues rather than inventing something new.Screenshots (if appropriate):
New Storybook story
Components/Forms/Switch → LabelStartcovers it (Chromatic will diff it); to follow.Testing approaches
pnpm vitest run packages/components/__tests__/Switch.spec.tsx— 7 passed, including new cases for the default (label after track) andlabelPosition="start".pnpm typecheck,pnpm oxlint:js:pathon the changed files,pnpm fmt:check,pnpm lint:css:path packages/components/src/styles/Switch.module.css.minoron@launchpad-ui/components).Follow-up: once this is released, gonfalon can drop its local
row-reverseoverride in favor of the prop.Link to Devin session: https://app.devin.ai/sessions/6458b07f375e44219f97c3a2beb73491
Requested by: @hsadhvani
Note
Overview
Adds
labelPosition(start|end, defaultend) onSwitchso visiblechildrencan render before the track without leaving the<label>.Layout uses a
labelStartclass that setsorder: 1on the track (notrow-reverse), keeping the label clickable and the accessible name aligned with the visible text. On/Off track internals are unchanged.Tests, Storybook stories, and a minor changeset for
@launchpad-ui/componentsare included.Reviewed by Cursor Bugbot for commit 2fa9404. Bugbot is set up for automated code reviews on this repo. Configure here.