packages/react-components/react-switch/library/docs/Spec.md
GitHub Epic issue - Switch Convergence #19409
Previously called Toggle in v8 and Checkbox in v0, the Switch component introduces a quick way of switching between on/off states by clicking/tapping the thumb.
The Open UI Switch Research page shows that the component is used in UI platforms across the web, with the Switch moniker being the most prominently used across major component libraries.
The existing components are:
The v8 Toggle component
supports both indeterminate and checked states. In this case, an input tag is used and its opacity is set to 0. This allows for the logic to be performed by the native element while a div is rendered to show the styled checkbox.
Example
<Checkbox
label="Indeterminate checkbox (controlled)"
indeterminate={isIndeterminate}
checked={isChecked}
onChange={onChange}
/>
Component props:
| Prop | Description |
|---|---|
ariaLabel | Text for screen-reader to announce as the name of the toggle. |
as | Render the root element as another type. |
checked | Checked state of the toggle. If you are maintaining state yourself, use this property. Otherwise use defaultChecked. |
componentRef | Optional callback to access the IToggle interface. Use this instead of ref for accessing the public methods and properties of the component. |
defaultChecked | Initial state of the toggle. If you want the toggle to maintain its own state, use this. Otherwise use checked. |
disabled | Optional disabled flag. |
inlineLabel | Whether the label (not the onText/offText) should bbe positioned inline with the toggle control. |
label | A label for the toggle. |
offText | Text to display when toggle is OFF. |
onChange | Callback issued when the value changes. |
onText | Text to display when toggle is ON. |
role | Whether to use the 'switch' role (ARIA 1.1) or the 'checkbox' role (ARIA 1.0). |
styles | Optional styles for the component. |
theme | Theme provided by HOC. |
The v0 Checkbox component
supports a mixed state, which is the same as indeterminate in v8. It is rendered as a div with role="checkbox" and does not use the native input element.
// string
<Checkbox label="Make my profile visible" />
// jsx
<Checkbox
label={
<span>
Long labels will wrap and the indicator
should remain top-aligned.
</span>
}
/>
Component props:
| Prop | Description |
|---|---|
accessibility | Accessibility behavior if overridden by the user. |
as | Render as given string or component. |
checked | Checkbox's checked state. |
className | Additional styles. |
defaultChecked | Whether the checkbox should be set to checked by default. |
design | ... |
disabled | Whether the checkbox is disabled or not. |
indicator | Checkbox's icon indicator. |
label | Label text or jsx to be rendered in the label. |
labelPosition | Whether the label is rendered on left or right (start or end). |
onChange | Event handler to be called after checked state has changed. |
onClick | Event handler to be called after the checkbox is clicked. |
styles | Additional styles. |
toggle | Render a toggle style checkbox with on and off choices. |
variables | Additional styles. |
<Switch checked />
<Switch checked disabled/>
<Switch checked onChange={onChange}/>
See API at Switch.types.ts.
root - The outer <div> wrapping the indicator, input and label to provide the correct layout styling.indicator - The track and the thumb sliding over it indicating the on and off status of the Switch.input - The visually hidden <input type="checkbox"> that handles the Switch's functionality. This is the primary slot: it receives all of the native props passed to the Switch component. It has opacity 0 and overlaps the entire root slot, for hit testing.label - (optional) The <label> describing this Switch.<Switch checked={true} />
<slots.root {...slotProps.root}>
<slots.input {...slotProps.input} />
{labelPosition !== 'after' && slots.label && <slots.label {...slotProps.label} />}
<slots.indicator {...slotProps.indicator} />
{labelPosition === 'after' && slots.label && <slots.label {...slotProps.label} />}
</slots.root>
With label before the track thumb indicator:
<div class="fui-Switch">
<input class="fui-Switch__input" id="switch-1" role="switch" type="checkbox" />
<label class="fui-Switch__label" for="switch-1" />
<div aria-hidden="true" class="fui-Switch__indicator">
<CircleFilled />
</div>
</div>
With label after the track thumb indicator:
<div class="fui-Switch">
<input class="fui-Switch__input" id="switch-1" role="switch" type="checkbox" />
<div aria-hidden="true" class="fui-Switch__indicator">
<CircleFilled />
</div>
<label class="fui-Switch__label" for="switch-1" />
</div>
See MIGRATION.md.
The following section describes the different states in which a Switch can be throughout the course of interaction with it.
An enabled Switch communicates interaction by having styling that invites the user to click/tap on it to toggle between on/off states.
A disabled Switch is non-interactive, ignoring all events and never updating its value. It does not allow focus and changes its styling to indicates this lack of interaction.
A hovered Switch changes styling to communicate that the user has placed a cursor above it.
A pressed Switch changes styling to communicate that the user is currently pressing it.
An unchecked Switch has the thumb on the left (right in RTL) and styling to indicate that it is off.
A checked Switch has the thumb on the right (left in RTL) and styling to indicate that it is on.
The following is a set of keys that interact with the Switch component:
| Key | Description |
|---|---|
Space | Switches between on/off states. |
click: Triggers a toggle between on and off values. The thumb animates from left to right [off > on] and right to left [on > off] to reflect this change (the directions are reversed in RTL).The same behavior as above is traslated for touch events.
<input type="checkbox"> with role="switch" set and does not require any additional handling for aria on the input element.aria-hidden="true" as it is a purely visual representation of the state of the underlying input.