Aksara UI

Switch

Turns a setting on or off, with an optional label and description.

import { Switch } from '@aksornproject/aksara-ui'

Basic switch

Without a label the Switch renders the track, the thumb and a hidden input. checked, defaultChecked and onCheckedChange work as in Base UI, and onCheckedChange receives the next boolean first. A bare switch needs an aria-label.

<Switch checked={on} onCheckedChange={setOn} aria-label="Show answers" />

Label and description

label renders a <label> linked to the hidden input. Clicking it toggles the switch, and it names the switch. description renders under the label and is linked with aria-describedby. TypeScript accepts description, labelPosition and labelSpacing only next to a label.

<Switch
  label="Remember me"
  description="Save my login details for next time."
/>

Settings rows

labelPosition="start" puts the text before the switch, in the DOM as well as on screen. labelSpacing="spread" fills the parent and pins the switch to the far end. The default, hug, fits the text up to 384px wide.

<div className="flex w-full max-w-spacing-384 flex-col gap-spacing-16">
  <Switch
    label="Email digests"
    description="A summary of class activity every Friday."
    labelPosition="start"
    labelSpacing="spread"
    defaultChecked
  />
  <Switch
    label="Homework reminders"
    labelPosition="start"
    labelSpacing="spread"
  />
</div>

Sizes

medium is 32 by 20px and the default. large is 40 by 24px. The thumb grows when the switch turns on.

<Switch aria-label="Medium, off" />
<Switch aria-label="Medium, on" defaultChecked />
<Switch size="large" aria-label="Large, off" />
<Switch size="large" aria-label="Large, on" defaultChecked />

Success status

status="success" recolours the on track and the focus ring. It draws an X in the thumb while off and a check while on.

<Switch status="success" aria-label="Verified, off" />
<Switch status="success" aria-label="Verified, on" defaultChecked />

Loading

loading draws a spinner in the thumb, sets aria-busy and blocks every toggle. Focus stays on the switch. Use it while a change waits for the server.

<Switch
  label="Sync grades"
  checked={on}
  loading={saving}
  onCheckedChange={save}
/>

Icons

checkedIcon and uncheckedIcon fill the thumb while on and while off. Leave one out to keep the status default. Pass null to clear it, such as the X of a success switch.

<Switch
  size="large"
  aria-label="Notifications"
  defaultChecked
  checkedIcon={<IconInterfaceEssentialBell3 />}
  uncheckedIcon={<IconInterfaceEssentialBellCrossDelete />}
/>
<Switch
  status="success"
  size="large"
  aria-label="Verified"
  uncheckedIcon={null}
/>

States

disabled greys the switch. The off track is pale with a border and a grey thumb, and the on track is grey with a white thumb. Its cursor is not-allowed. readOnly keeps the default colours without hover and blocks toggling.

<div className="flex flex-col gap-spacing-16">
  <Switch label="Disabled" disabled />
  <Switch label="Disabled and on" disabled defaultChecked />
  <Switch label="Read-only" readOnly defaultChecked />
</div>

Accessibility

  • The switch has role="switch" and aria-checked. Space and Enter toggle it.
  • Name every switch with label or aria-label. The types do not require a name, so nothing warns you when it is missing.
  • description joins aria-describedby after any ids you pass.
  • The icons and the spinner are hidden from screen readers. The state comes from aria-checked.
  • The focus ring draws on keyboard focus only. The deprecated focused prop draws it without focus.
  • The thumb and track stop animating under prefers-reduced-motion.

API

Switch

className styles the track and thumbClassName the thumb. SwitcherProps is an alias of the same type. Use SwitchProps for type annotations.

Prop

Type

Edit on GitHub

On this page