Switch

ui-switch is a native <input type="checkbox" role="switch"> drawn as a sliding toggle, with the element's text as its label. Use it for a setting that takes effect right away. Use Checkbox for choices that are confirmed later with a submit button.

It is form-associated. When on, it submits its value (or on) under its name, like a checkbox.

Previewindex.html
Airplane mode
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <ui-switch>Airplane mode</ui-switch>
</body>
</html>

Examples

Checked

Previewindex.html
Wi-Fi
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <ui-switch checked>Wi-Fi</ui-switch>
</body>
</html>

Small

Previewindex.html
Compact mode
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <ui-switch size="sm" checked>Compact mode</ui-switch>
</body>
</html>

Disabled

Previewindex.html
Bluetooth Location
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <ui-switch disabled>Bluetooth</ui-switch>
  <ui-switch disabled checked>Location</ui-switch>
</body>
</html>

Without visible text

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <ui-switch aria-label="Dark mode"></ui-switch>
</body>
</html>

Listening for changes

Previewindex.htmlswitch-change-demo.html
Sync files

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <import-element from="./switch-change-demo.html"></import-element>
  <switch-change-demo></switch-change-demo>
</body>
</html>
<custom-element name="switch-change-demo">
  <script>
    const sync = useState({ status: "Sync is paused." });

    function changed(control) {
      sync.status = control.checked ? "Sync is running." : "Sync is paused.";
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 2);
    }
    p {
      margin: 0;
      font-size: var(--text-sm);
      color: var(--muted-foreground);
    }
  </style>

  <ui-switch onchange="changed(this)">Sync files</ui-switch>
  <p role="status"><slot name="#sync.status"></slot></p>
</custom-element>

In a form

Only switches that are on are submitted. Reset restores the checked attribute.

Previewindex.htmlswitch-form-demo.html
Email notifications Push notifications
Save Reset
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Switch demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/switch.html"></import-element>

  <import-element from="./switch-form-demo.html"></import-element>
  <switch-form-demo></switch-form-demo>
</body>
</html>
<custom-element name="switch-form-demo">
  <script>
    const result = useState({ text: "Submit the form to see its data." });

    function submit(event) {
      event.preventDefault();
      const entries = [...new FormData(event.target)].map(([name, value]) => `${name}=${value}`);
      result.text = entries.join("\n") || "(nothing submitted)";
    }

    function reset() {
      result.text = "The form was reset.";
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 4);
      width: 100%;
      max-width: calc(var(--spacing) * 80);
    }
    form {
      display: grid;
      gap: calc(var(--spacing) * 3);
      justify-items: start;
    }
    .actions {
      display: flex;
      gap: calc(var(--spacing) * 2);
    }
    output {
      display: block;
      padding: calc(var(--spacing) * 3);
      border-radius: var(--radius-md);
      background: var(--muted);
      font-family: var(--font-mono);
      font-size: var(--text-xs);
      white-space: pre-wrap;
    }
  </style>

  <form onsubmit="submit(event)" onreset="reset()">
    <ui-switch name="email" checked>Email notifications</ui-switch>
    <ui-switch name="push" value="enabled">Push notifications</ui-switch>
    <div class="actions">
      <ui-button type="submit">Save</ui-button>
      <ui-button type="reset" variant="outline">Reset</ui-button>
    </div>
  </form>
  <output><slot name="#result.text"></slot></output>
</custom-element>

API

Attributes

Attribute Type Default Description
checked boolean false Whether the switch is on. The attribute is the initial and reset state.
value string Value submitted when on. Without it the form submits on.
size "default" | "sm" "default" Track and thumb size.
disabled boolean false Disables the switch. Also true inside a disabled <fieldset>.
aria-label string Accessible name when there is no visible text. Property name: label.

Properties

Property Type Description
checked boolean Current state. Updated when the user toggles. Setting it moves the switch.

Events

Event Detail Description
change none Bubbles from the element after the user toggles. checked is already updated.
input none The inner checkbox's native input event (composed).

Slots

Slot Description
(default) The label text, rendered inside a native <label> for the switch.

CSS parts

Part Description
control The inner <input type="checkbox" role="switch">.
label The <label> around the slotted text.

CSS states

State Description
:state(attr-checked) Set while the switch is on.
:state(attr-size-is-sm) Set for the small size.

Forms

ui-switch is form-associated. When on it submits value, or on without one. When off it submits nothing. form.reset() restores the checked attribute.

Accessibility

The inner checkbox has role="switch" and aria-checked, so screen readers announce it as on or off. Space toggles it and the host delegates focus to it. The slotted text labels it. Without text, use aria-label.

Edit this page on GitHub · View as Markdown