Switch

A switch turns one setting on or off, and the change takes effect the moment someone flips it. Adding icons to the handle, a check or a check and a cross, lets people read the state without relying on the track color. The switches below are live, with and without labels, and some change on a timer.

Wi-Fi Alerts Dark mode Beta features Location Sync Reminders Haptics Hotspot Autoplay Eco mode Offline Keep me signed in Subtitles Share my screen Updates Send me the newsletter

Reference docs⁠

BlazorReactWebOverviewExamplesAPIStylingAccessibility

Choosing a selection control⁠

Four components capture a choice in a form. Each suits a different job:

  • Checkbox: one or more options picked from a short list, or a single yes to confirm, such as "I agree to the terms".
  • Switch: a setting that takes effect the moment it is flipped, such as Wi-Fi or dark mode.
  • Radio buttons: exactly one option from a short list shown in full, such as delivery speed.
  • Select: exactly one option from a longer list kept closed until opened, such as country.

Two jobs belong elsewhere. A choice made by clicking a compact tag, such as filtering results, is a filter chip, and a two-state setting in a toolbar is a toggle button.

Built in⁠

  • icon-selection puts a check, or a check and a cross, on the handle, so the state does not rest on color alone.
  • The default slot is the label, and a click on the label toggles the switch.
  • Space and Enter both toggle the switch, and a focus ring draws on keyboard focus.
  • badge-value, badge-dot, and badge-concealed add a badge with no separate element to place.
  • name and form-value set what a form submits while the switch is selected, and form-is-required makes it invalid while off.
  • Its touch target is at least 48 by 48 pixels even though the switch paints smaller (touch-target).

Using switches well⁠

  • Prefer a switch to a checkbox for settings on a phone.
  • Label every switch with the setting it controls, in visible text or in aria-label.
  • Add icons only to emphasize the selected state: a check shown while selected, or a check and a cross in both states.

These rules follow Material 3 Expressive's guidance for switches, on material.io.

Touch target⁠

touch-target sets the area a finger can press, and the switch paints the same with every value. overflow lets an area of at least 48 by 48 pixels spill past the switch, and wrapper makes the element take up that space. none keeps the target at the painted size. Omit it and your style configuration supplies the value, overflow unless you changed it. Pointer and touch says how this measures against WCAG 2.5.8.

Icons⁠

icon-selection marks the state on the handle. none draws a plain handle, selected draws a check while selected is set, and both draws a check when on and a cross when off. Omit it and the switch uses none.

Put your own icons in the on-icon and off-icon content, using an cw-icon or an equivalent. In the demo, the four switches share one selected value, so flipping any of them flips all.

Badges⁠

badge-value puts a short count or label on the switch's corner, with no separate element to position. badge-dot shows a small dot instead, for "something new" without a number. badge-concealed hides the badge and keeps its value, so clearing it shows the same badge again.

The badge stays out of the switch's accessible name; Names and states says where to put a count that matters.

Forms⁠

A switch in a form submits form-value under its name while selected is set, and submits nothing while it is off. form-value is on unless you set it. form-is-required makes the switch invalid while it is off, for a setting the form cannot go ahead without.

The preview runs in a sandbox that blocks form submission, so the demo shows each switch's state below the form instead. Flip the switches and the state follows; Reset returns them to their authored state.

Disabled⁠

disabled takes the switch out of the Tab order and blocks toggling, so nothing can focus or flip it. Use it when the setting is unavailable. A disabled <fieldset> disables the switch too, and disabled then reads true without being set on the switch. In the demo, turning airplane mode on disables the other two switches.

Label position⁠

label-position puts the label before or after the switch. leading is the default, which differs from a checkbox, and trailing puts the label after the switch. A switch with no label text needs an aria-label.

In a settings list⁠

Settings take effect the moment they flip, so a settings list needs no Save button: each row is a switch with a label that states the setting. Keep the labels short, in one position for the whole list, and give each setting its own switch.

Properties⁠

PropertyAttributeTypeDescription
badgeConcealedbadge-concealedbooleandefault falseHides the badge while keeping its value and dot setting, so clearing this property shows the same badge again. See Badges.
badgeDotbadge-dotbooleandefault falseShows the badge as a small dot with no text, whether or not badgeValue is set. See Badges.
badgeValuebadge-valuestringThe badge's text. While it is empty and badgeDot is not set, no badge renders. Ignored if badgeDot is set. See Badges.
disableddisabledbooleanWhether the element is disabled. Also reads true while a disabled <fieldset> contains it. See Disabled.
formread-onlyHTMLFormElementThe form the component belongs to, or null when it has none.
formIsRequiredform-is-requiredbooleandefault falseWhen true, require the switch to be selected when participating in form submission.See MDN: checkbox validation. See Forms.
formValueform-valuestringdefault onThe value of the switch that is submitted with a form when selected.See MDN: checkbox value. See Forms.
iconSelectionicon-selectionnone, selected, bothIconSelectionIconSelection.None, .Selected, .Bothdefault IconSelection.NoneSets when the switch's handle shows an icon: never, only while the switch is on, or always. See Icons.
labelPositionlabel-positionleading, trailingLabelPositionLabelPosition.Leading, .Trailingdefault LabelPosition.LeadingSets which side of the switch the label is on. See Label position.
labelsread-onlyNodeListThe <label> elements associated with the element.
namenamestringThe name the element's value is submitted under with its form. See Forms.
selectedselectedbooleandefault falseWhether the switch is on. A user's toggle updates it, and a form reset restores the state the selected attribute gave it. See Icons, Forms.
touchTargettouch-targetnone, overflow, wrapperTouchTargetTouchTarget.None, .Overflow, .Wrapperdefault TouchTarget.Overflow, from the style configurationThe area that takes a touch, at least 48 by 48 pixels and centered on the component. overflow extends it past the component without changing the component's layout size, wrapper makes the component take up the whole area in the layout, and none limits it to the component's own size. See Touch target.
validationMessageread-onlystringThe message describing why the element is invalid, or an empty string when it is valid.
validityread-onlyValidityStateThe element's ValidityState.
willValidateread-onlybooleanWhether the element takes part in constraint validation. False while it is disabled or read-only.

The value and checked getters are read-only boolean mirrors of selected, not the string a form submits.

Slots⁠

SlotDescription
(default)The label content.
on-iconThe icon for the switch's "on" state, replacing the default check mark. It shows only when icon-selection is selected or both. Intended for a cw-icon or equivalent.
off-iconThe icon for the switch's "off" state, replacing the default close mark. It shows only when icon-selection is both. Intended for a cw-icon or equivalent.

Events⁠

EventTypeDetailDescription
changeEvent—Fired after a user toggles the switch. Setting selected from code does not fire it. It bubbles.
inputInputEvent—Fired as a user toggles the switch. Setting selected from code does not fire it. It bubbles and is composed.

Methods⁠

MethodReturnsDescription
checkValidity()booleanChecks the element's validity and returns true if it is valid. If not, it dispatches an invalid event; nothing changes on screen.
reportValidity()booleanChecks the element's validity and returns true if it is valid. If not, it dispatches a cancelable invalid event and, unless a listener cancels it, shows validationMessage as the field's error text and focuses the field.
setCustomValidity(error: string)voidSets a custom validation message, which makes the element invalid. An empty string clears it.

Props⁠

PropTypeDescription
badgeConcealedbooleandefault falseHides the badge while keeping its value and dot setting, so clearing this property shows the same badge again. See Badges.
badgeDotbooleandefault falseShows the badge as a small dot with no text, whether or not badgeValue is set. See Badges.
badgeValuestringThe badge's text. While it is empty and badgeDot is not set, no badge renders. Ignored if badgeDot is set. See Badges.
disabledbooleanWhether the element is disabled. Also reads true while a disabled <fieldset> contains it. See Disabled.
formread-only (ref)HTMLFormElementThe form the component belongs to, or null when it has none.
formIsRequiredbooleandefault falseWhen true, require the switch to be selected when participating in form submission.See MDN: checkbox validation. See Forms.
formValuestringdefault onThe value of the switch that is submitted with a form when selected.See MDN: checkbox value. See Forms.
iconSelectionIconSelectionTypeIconSelection.None, .Selected, .Bothdefault IconSelection.NoneSets when the switch's handle shows an icon: never, only while the switch is on, or always. See Icons.
labelPositionLabelPositionTypeLabelPosition.Leading, .Trailingdefault LabelPosition.LeadingSets which side of the switch the label is on. See Label position.
labelsread-only (ref)NodeListThe <label> elements associated with the element.
namestringThe name the element's value is submitted under with its form. See Forms.
selectedbooleandefault falseWhether the switch is on. A user's toggle updates it, and a form reset restores the state the selected attribute gave it. See Icons, Forms.
touchTargetTouchTargetTypeTouchTarget.None, .Overflow, .Wrapperdefault TouchTarget.Overflow, from the style configurationThe area that takes a touch, at least 48 by 48 pixels and centered on the component. overflow extends it past the component without changing the component's layout size, wrapper makes the component take up the whole area in the layout, and none limits it to the component's own size. See Touch target.
validationMessageread-only (ref)stringThe message describing why the element is invalid, or an empty string when it is valid.
validityread-only (ref)ValidityStateThe element's ValidityState.
willValidateread-only (ref)booleanWhether the element takes part in constraint validation. False while it is disabled or read-only.

The value and checked getters are read-only boolean mirrors of selected, not the string a form submits.

Children⁠

ChildDescription
childrenThe label content.
a child with slot="on-icon"The icon for the switch's "on" state, replacing the default check mark. It shows only when iconSelection is selected or both. Intended for a CrIcon or equivalent.
a child with slot="off-icon"The icon for the switch's "off" state, replacing the default close mark. It shows only when iconSelection is both. Intended for a CrIcon or equivalent.

Events⁠

PropArgumentDescription
onChangeReact.ChangeEvent<HTMLInputElement>Fired after a user toggles the switch. Setting selected from code does not fire it. It bubbles.
onInputReact.ChangeEvent<HTMLInputElement>Fired as a user toggles the switch. Setting selected from code does not fire it. It bubbles and is composed.

Methods⁠

Call these on the element the ref holds.

MethodReturnsDescription
checkValidity()booleanChecks the element's validity and returns true if it is valid. If not, it dispatches an invalid event; nothing changes on screen.
reportValidity()booleanChecks the element's validity and returns true if it is valid. If not, it dispatches a cancelable invalid event and, unless a listener cancels it, shows validationMessage as the field's error text and focuses the field.
setCustomValidity(error: string)voidSets a custom validation message, which makes the element invalid. An empty string clears it.

Parameters⁠

ParameterTypeDescription
BadgeConcealedbool?default falseHides the badge while keeping its value and dot setting, so clearing this property shows the same badge again. See Badges.
BadgeDotbool?default falseShows the badge as a small dot with no text, whether or not BadgeValue is set. See Badges.
BadgeValuestring?The badge's text. While it is empty and BadgeDot is not set, no badge renders. Ignored if BadgeDot is set. See Badges.
Disabledbool?Whether the element is disabled. Also reads true while a disabled <fieldset> contains it. See Disabled.
FormIsRequiredbool?default falseWhen true, require the switch to be Selected when participating in form submission.See MDN: checkbox validation. See Forms.
FormValuestring?default onThe value of the switch that is submitted with a form when selected.See MDN: checkbox value. See Forms.
IconSelectionIconSelection?IconSelection.None, .Selected, .Bothdefault IconSelection.NoneSets when the switch's handle shows an icon: never, only while the switch is on, or always. See Icons.
LabelPositionLabelPosition?LabelPosition.Leading, .Trailingdefault LabelPosition.LeadingSets which side of the switch the label is on. See Label position.
Namestring?The name the element's value is submitted under with its form. See Forms.
TouchTargetTouchTarget?TouchTarget.None, .Overflow, .Wrapperdefault TouchTarget.Overflow, from the style configurationThe area that takes a touch, at least 48 by 48 pixels and centered on the component. overflow extends it past the component without changing the component's layout size, wrapper makes the component take up the whole area in the layout, and none limits it to the component's own size. See Touch target.
Valuebool—
ValueExpressionExpression<Func<bool>>?Gets or sets an expression that identifies the bound value.

The value and checked getters are read-only boolean mirrors of selected, not the string a form submits.

Render fragments⁠

Once you write OffIcon or any other fragment as a child element, the main content must go in an explicit <ChildContent> too; markup beside <OffIcon> is a compile error. Here ChildContent is: The label content.

<CbSwitch>
    <OffIcon><CbIcon>favorite</CbIcon></OffIcon>
    <ChildContent>Main content</ChildContent>
</CbSwitch>
content_copy
ParameterTypeDescription
ChildContentRenderFragment?The label content.
OffIconRenderFragment?The icon for the switch's "off" state, replacing the default close mark. It shows only when IconSelection is both. Intended for a CbIcon or equivalent.
OnIconRenderFragment?The icon for the switch's "on" state, replacing the default check mark. It shows only when IconSelection is Selected or both. Intended for a CbIcon or equivalent.

Event callbacks⁠

ParameterTypeDescription
OnChangeEventCallback<ChangeEventArgs>Fired after a user toggles the switch. Setting Selected from code does not fire it. It bubbles.
OnInputEventCallback<ChangeEventArgs>Fired as a user toggles the switch. Setting Selected from code does not fire it. It bubbles and is composed.

Inputs⁠

InputTypeDescription
badgeConcealedbooleandefault falseHides the badge while keeping its value and dot setting, so clearing this property shows the same badge again. See Badges.
badgeDotbooleandefault falseShows the badge as a small dot with no text, whether or not badgeValue is set. See Badges.
badgeValuestringThe badge's text. While it is empty and badgeDot is not set, no badge renders. Ignored if badgeDot is set. See Badges.
disabledbooleanWhether the element is disabled. Also reads true while a disabled <fieldset> contains it. See Disabled.
formIsRequiredbooleandefault falseWhen true, require the switch to be selected when participating in form submission.See MDN: checkbox validation. See Forms.
formValuestringdefault onThe value of the switch that is submitted with a form when selected.See MDN: checkbox value. See Forms.
iconSelectionIconSelectionTypeIconSelection.None, .Selected, .Bothdefault IconSelection.NoneSets when the switch's handle shows an icon: never, only while the switch is on, or always. See Icons.
labelPositionLabelPositionTypeLabelPosition.Leading, .Trailingdefault LabelPosition.LeadingSets which side of the switch the label is on. See Label position.
namestringThe name the element's value is submitted under with its form. See Forms.
selectedbooleandefault falseWhether the switch is on. A user's toggle updates it, and a form reset restores the state the selected attribute gave it. See Icons, Forms.
touchTargetTouchTargetTypeTouchTarget.None, .Overflow, .Wrapperdefault TouchTarget.Overflow, from the style configurationThe area that takes a touch, at least 48 by 48 pixels and centered on the component. overflow extends it past the component without changing the component's layout size, wrapper makes the component take up the whole area in the layout, and none limits it to the component's own size. See Touch target.
valueboolean—

The value and checked getters are read-only boolean mirrors of selected, not the string a form submits.

Content projection⁠

ContentDescription
content with no slotThe label content.
an element with slot="on-icon"The icon for the switch's "on" state, replacing the default check mark. It shows only when iconSelection is selected or both. Intended for a CaIcon or equivalent.
an element with slot="off-icon"The icon for the switch's "off" state, replacing the default close mark. It shows only when iconSelection is both. Intended for a CaIcon or equivalent.

Outputs⁠

OutputEventDescription
cwChangeEventFired after a user toggles the switch. Setting selected from code does not fire it. It bubbles.
cwInputInputEventFired as a user toggles the switch. Setting selected from code does not fire it. It bubbles and is composed.

Props⁠

PropTypeDescription
badgeConcealedbooleandefault falseHides the badge while keeping its value and dot setting, so clearing this property shows the same badge again. See Badges.
badgeDotbooleandefault falseShows the badge as a small dot with no text, whether or not badgeValue is set. See Badges.
badgeValuestringThe badge's text. While it is empty and badgeDot is not set, no badge renders. Ignored if badgeDot is set. See Badges.
disabledbooleanWhether the element is disabled. Also reads true while a disabled <fieldset> contains it. See Disabled.
formIsRequiredbooleandefault falseWhen true, require the switch to be selected when participating in form submission.See MDN: checkbox validation. See Forms.
formValuestringdefault onThe value of the switch that is submitted with a form when selected.See MDN: checkbox value. See Forms.
iconSelectionIconSelectionTypeIconSelection.None, .Selected, .Bothdefault IconSelection.NoneSets when the switch's handle shows an icon: never, only while the switch is on, or always. See Icons.
labelPositionLabelPositionTypeLabelPosition.Leading, .Trailingdefault LabelPosition.LeadingSets which side of the switch the label is on. See Label position.
namestringThe name the element's value is submitted under with its form. See Forms.
selectedbooleandefault falseWhether the switch is on. A user's toggle updates it, and a form reset restores the state the selected attribute gave it. See Icons, Forms.
touchTargetTouchTargetTypeTouchTarget.None, .Overflow, .Wrapperdefault TouchTarget.Overflow, from the style configurationThe area that takes a touch, at least 48 by 48 pixels and centered on the component. overflow extends it past the component without changing the component's layout size, wrapper makes the component take up the whole area in the layout, and none limits it to the component's own size. See Touch target.

The value and checked getters are read-only boolean mirrors of selected, not the string a form submits.

Slots⁠

SlotDescription
defaultThe label content.
off-iconThe icon for the switch's "off" state, replacing the default close mark. It shows only when iconSelection is both. Intended for a CvIcon or equivalent.
on-iconThe icon for the switch's "on" state, replacing the default check mark. It shows only when iconSelection is selected or both. Intended for a CvIcon or equivalent.

Events⁠

EventTypeDescription
changeEventFired after a user toggles the switch. Setting selected from code does not fire it. It bubbles.
inputInputEventFired as a user toggles the switch. Setting selected from code does not fire it. It bubbles and is composed.

Props⁠

PropTypeDescription
badgeConcealedbooleandefault falseHides the badge while keeping its value and dot setting, so clearing this property shows the same badge again. See Badges.
badgeDotbooleandefault falseShows the badge as a small dot with no text, whether or not badgeValue is set. See Badges.
badgeValuestringThe badge's text. While it is empty and badgeDot is not set, no badge renders. Ignored if badgeDot is set. See Badges.
disabledbooleanWhether the element is disabled. Also reads true while a disabled <fieldset> contains it. See Disabled.
formIsRequiredbooleandefault falseWhen true, require the switch to be selected when participating in form submission.See MDN: checkbox validation. See Forms.
formValuestringdefault onThe value of the switch that is submitted with a form when selected.See MDN: checkbox value. See Forms.
iconSelectionIconSelectionTypeIconSelection.None, .Selected, .Bothdefault IconSelection.NoneSets when the switch's handle shows an icon: never, only while the switch is on, or always. See Icons.
labelPositionLabelPositionTypeLabelPosition.Leading, .Trailingdefault LabelPosition.LeadingSets which side of the switch the label is on. See Label position.
namestringThe name the element's value is submitted under with its form. See Forms.
selectedbooleandefault falseWhether the switch is on. A user's toggle updates it, and a form reset restores the state the selected attribute gave it. See Icons, Forms.
touchTargetTouchTargetTypeTouchTarget.None, .Overflow, .Wrapperdefault TouchTarget.Overflow, from the style configurationThe area that takes a touch, at least 48 by 48 pixels and centered on the component. overflow extends it past the component without changing the component's layout size, wrapper makes the component take up the whole area in the layout, and none limits it to the component's own size. See Touch target.

The value and checked getters are read-only boolean mirrors of selected, not the string a form submits.

Snippets⁠

SnippetDescription
childrenThe label content.
onIconSnippetThe icon for the switch's "on" state, replacing the default check mark. It shows only when iconSelection is selected or both. Intended for a CsIcon or equivalent.
offIconSnippetThe icon for the switch's "off" state, replacing the default close mark. It shows only when iconSelection is both. Intended for a CsIcon or equivalent.

Events⁠

PropTypeDescription
onChangeEventFired after a user toggles the switch. Setting selected from code does not fire it. It bubbles.
onInputInputEventFired as a user toggles the switch. Setting selected from code does not fire it. It bubbles and is composed.

A value is what the token defaults to; selecting it copies the token's name.

Tokens⁠

TokenValue
--ct-switch-corner-end-end-shapesame as --ct-switch-corner-shape --ct-switch-corner-end-end-shape
--ct-switch-corner-end-start-shapesame as --ct-switch-corner-shape --ct-switch-corner-end-start-shape
--ct-switch-corner-shape--ct-sys-corner-shape --ct-switch-corner-shape
--ct-switch-corner-start-end-shapesame as --ct-switch-corner-shape --ct-switch-corner-start-end-shape
--ct-switch-corner-start-start-shapesame as --ct-switch-corner-shape --ct-switch-corner-start-start-shape
--ct-switch-disabled-handle-color--ct-sys-color-on-surface --ct-switch-disabled-handle-color
--ct-switch-disabled-handle-opacity0.38 --ct-switch-disabled-handle-opacity
--ct-switch-disabled-icon-color--ct-sys-color-surface-container-highest --ct-switch-disabled-icon-color
--ct-switch-disabled-icon-opacity0.38 --ct-switch-disabled-icon-opacity
--ct-switch-disabled-label-text-color--ct-sys-color-on-surface --ct-switch-disabled-label-text-color
--ct-switch-disabled-label-text-opacity0.38 --ct-switch-disabled-label-text-opacity
--ct-switch-disabled-selected-handle-color--ct-sys-color-surface --ct-switch-disabled-selected-handle-color
--ct-switch-disabled-selected-handle-opacity1 --ct-switch-disabled-selected-handle-opacity
--ct-switch-disabled-selected-icon-color--ct-sys-color-on-surface --ct-switch-disabled-selected-icon-color
--ct-switch-disabled-selected-icon-opacity0.38 --ct-switch-disabled-selected-icon-opacity
--ct-switch-disabled-selected-track-color--ct-sys-color-on-surface --ct-switch-disabled-selected-track-color
--ct-switch-disabled-track-color--ct-sys-color-surface-container-highest --ct-switch-disabled-track-color
--ct-switch-disabled-track-opacity0.12 --ct-switch-disabled-track-opacity
--ct-switch-disabled-track-outline-color--ct-sys-color-on-surface --ct-switch-disabled-track-outline-color
--ct-switch-focus-handle-color--ct-sys-color-on-surface-variant --ct-switch-focus-handle-color
--ct-switch-focus-icon-color--ct-sys-color-surface-container-highest --ct-switch-focus-icon-color
--ct-switch-focus-track-color--ct-sys-color-surface-container-highest --ct-switch-focus-track-color
--ct-switch-focus-track-outline-color--ct-sys-color-outline --ct-switch-focus-track-outline-color
--ct-switch-handle-color--ct-sys-color-outline --ct-switch-handle-color
--ct-switch-handle-corner-end-end-shapesame as --ct-switch-handle-corner-shape --ct-switch-handle-corner-end-end-shape
--ct-switch-handle-corner-end-start-shapesame as --ct-switch-handle-corner-shape --ct-switch-handle-corner-end-start-shape
--ct-switch-handle-corner-shapesame as --ct-switch-corner-shape --ct-switch-handle-corner-shape
--ct-switch-handle-corner-start-end-shapesame as --ct-switch-handle-corner-shape --ct-switch-handle-corner-start-end-shape
--ct-switch-handle-corner-start-start-shapesame as --ct-switch-handle-corner-shape --ct-switch-handle-corner-start-start-shape
--ct-switch-handle-end-end-radiussame as --ct-switch-handle-radius --ct-switch-handle-end-end-radius
--ct-switch-handle-end-start-radiussame as --ct-switch-handle-radius --ct-switch-handle-end-start-radius
--ct-switch-handle-height16px --ct-switch-handle-height
--ct-switch-handle-radiuscalc(50% - max(0px, var(--_track-height) / 2 - var(--_track-start-start-radius))) --ct-switch-handle-radius
--ct-switch-handle-start-end-radiussame as --ct-switch-handle-radius --ct-switch-handle-start-end-radius
--ct-switch-handle-start-start-radiussame as --ct-switch-handle-radius --ct-switch-handle-start-start-radius
--ct-switch-handle-width16px --ct-switch-handle-width
--ct-switch-hover-handle-color--ct-sys-color-on-surface-variant --ct-switch-hover-handle-color
--ct-switch-hover-icon-color--ct-sys-color-surface-container-highest --ct-switch-hover-icon-color
--ct-switch-hover-state-layer-color--ct-sys-color-on-surface --ct-switch-hover-state-layer-color
--ct-switch-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-switch-hover-state-layer-opacity
--ct-switch-hover-track-color--ct-sys-color-surface-container-highest --ct-switch-hover-track-color
--ct-switch-hover-track-outline-color--ct-sys-color-outline --ct-switch-hover-track-outline-color
--ct-switch-icon-color--ct-sys-color-surface-container-highest --ct-switch-icon-color
--ct-switch-icon-size16px --ct-switch-icon-size
--ct-switch-label-gap18px --ct-switch-label-gap
--ct-switch-label-text-color--ct-sys-color-on-surface --ct-switch-label-text-color
--ct-switch-label-text-font--ct-sys-typescale-label-large-font --ct-switch-label-text-font
--ct-switch-label-text-line-height--ct-sys-typescale-label-large-line-height --ct-switch-label-text-line-height
--ct-switch-label-text-size--ct-sys-typescale-label-large-size --ct-switch-label-text-size
--ct-switch-label-text-weight--ct-sys-typescale-label-large-weight --ct-switch-label-text-weight
--ct-switch-pressed-handle-color--ct-sys-color-on-surface-variant --ct-switch-pressed-handle-color
--ct-switch-pressed-handle-height28px --ct-switch-pressed-handle-height
--ct-switch-pressed-handle-width28px --ct-switch-pressed-handle-width
--ct-switch-pressed-icon-color--ct-sys-color-surface-container-highest --ct-switch-pressed-icon-color
--ct-switch-pressed-state-layer-color--ct-sys-color-on-surface --ct-switch-pressed-state-layer-color
--ct-switch-pressed-state-layer-opacity--ct-sys-state-pressed-state-layer-opacity --ct-switch-pressed-state-layer-opacity
--ct-switch-pressed-track-color--ct-sys-color-surface-container-highest --ct-switch-pressed-track-color
--ct-switch-pressed-track-outline-color--ct-sys-color-outline --ct-switch-pressed-track-outline-color
--ct-switch-selected-focus-handle-color--ct-sys-color-primary-container --ct-switch-selected-focus-handle-color
--ct-switch-selected-focus-icon-color--ct-sys-color-on-primary-container --ct-switch-selected-focus-icon-color
--ct-switch-selected-focus-track-color--ct-sys-color-primary --ct-switch-selected-focus-track-color
--ct-switch-selected-handle-color--ct-sys-color-on-primary --ct-switch-selected-handle-color
--ct-switch-selected-handle-height24px --ct-switch-selected-handle-height
--ct-switch-selected-handle-width24px --ct-switch-selected-handle-width
--ct-switch-selected-hover-handle-color--ct-sys-color-primary-container --ct-switch-selected-hover-handle-color
--ct-switch-selected-hover-icon-color--ct-sys-color-on-primary-container --ct-switch-selected-hover-icon-color
--ct-switch-selected-hover-state-layer-color--ct-sys-color-primary --ct-switch-selected-hover-state-layer-color
--ct-switch-selected-hover-state-layer-opacity--ct-sys-state-hover-state-layer-opacity --ct-switch-selected-hover-state-layer-opacity
--ct-switch-selected-hover-track-color--ct-sys-color-primary --ct-switch-selected-hover-track-color
--ct-switch-selected-icon-color--ct-sys-color-on-primary-container --ct-switch-selected-icon-color
--ct-switch-selected-icon-size16px --ct-switch-selected-icon-size
--ct-switch-selected-pressed-handle-color--ct-sys-color-primary-container --ct-switch-selected-pressed-handle-color
--ct-switch-selected-pressed-icon-color--ct-sys-color-on-primary-container --ct-switch-selected-pressed-icon-color
--ct-switch-selected-pressed-state-layer-color--ct-sys-color-primary --ct-switch-selected-pressed-state-layer-color
--ct-switch-selected-pressed-state-layer-opacity--ct-sys-state-pressed-state-layer-opacity --ct-switch-selected-pressed-state-layer-opacity
--ct-switch-selected-pressed-track-color--ct-sys-color-primary --ct-switch-selected-pressed-track-color
--ct-switch-selected-track-color--ct-sys-color-primary --ct-switch-selected-track-color
--ct-switch-state-layer-radius--ct-sys-corner-radius-full --ct-switch-state-layer-radius
--ct-switch-state-layer-size40px --ct-switch-state-layer-size
--ct-switch-track-color--ct-sys-color-surface-container-highest --ct-switch-track-color
--ct-switch-track-end-end-radiussame as --ct-switch-track-radius --ct-switch-track-end-end-radius
--ct-switch-track-end-start-radiussame as --ct-switch-track-radius --ct-switch-track-end-start-radius
--ct-switch-track-height32px --ct-switch-track-height
--ct-switch-track-outline-color--ct-sys-color-outline --ct-switch-track-outline-color
--ct-switch-track-outline-width2px --ct-switch-track-outline-width
--ct-switch-track-radius--ct-sys-corner-radius-full --ct-switch-track-radius
--ct-switch-track-start-end-radiussame as --ct-switch-track-radius --ct-switch-track-start-end-radius
--ct-switch-track-start-start-radiussame as --ct-switch-track-radius --ct-switch-track-start-start-radius
--ct-switch-track-width52px --ct-switch-track-width
--ct-switch-with-icon-handle-height24px --ct-switch-with-icon-handle-height
--ct-switch-with-icon-handle-width24px --ct-switch-with-icon-handle-width

Names and states⁠

A switch is announced with the switch role and follows the WAI-ARIA Authoring Practices: Switch pattern.

  • The accessible name is the label in the default slot, or aria-label. Give a switch without visible text an aria-label.
  • A <label for> elsewhere on the page does not name the switch. Use the default slot or aria-label.
  • aria-label is the only ARIA attribute a switch honors. Any other ARIA attribute you set on it has no effect, aria-invalid included.
  • selected is announced as on or off.
  • A badge is not part of the name, and a dot or concealed badge is hidden from assistive technology. Put a count that matters in the label.
  • The switch has no built-in tooltip. A standalone plain or rich tooltip anchored on it describes its focused control, so a screen reader announces that tooltip's text.

Keyboard and focus⁠

Key Action
Tab Moves focus to the switch, then past it.
Space Toggles the switch.
Enter Toggles the switch.

Space and Enter both toggle the switch; a disabled switch ignores both.

  • A focus ring draws on keyboard focus only.
  • disabled removes the switch from the Tab order and blocks toggling, Enter included.

Pointer and touch⁠

  • The touch target is at least 48 by 48 pixels, which is above the 24 by 24 pixels WCAG 2.5.8 asks for.
  • touch-target accepts none, overflow, and wrapper. none shrinks the target to the size the switch paints, so check that size before you choose it. Touch target shows each value.

Display settings⁠

  • In forced colors, a selected track fills with ButtonText, an unselected handle is ButtonText, and disabled parts take GrayText. The on icon is ButtonText and the off icon is Canvas.
  • With reduced motion requested, the handle slide, the handle resize, and the track and icon transitions take no time.

Standards⁠

With its defaults, the switch meets three WCAG 2.2 success criteria:

  • 2.1.1 Keyboard (A)
  • 2.4.7 Focus Visible (AA)
  • 2.5.8 Target Size (Minimum) (AA)

Two more depend on how you use it:

  • 1.1.1 Non-text Content (A): mark a custom icon in the on-icon or off-icon content as decorative with aria-hidden="true".
  • 4.1.2 Name, Role, Value (A): give a switch with no visible label text an aria-label.