Tessera UI

Components

Switch

A setting that is on or off, and takes effect at once. Press it, or use Space or Enter.

The preview needs JavaScript. The code is in the Code tab.

Switch-0.tess
<template>
  <div>
    <Switch>Notifications</Switch>
    <Switch checked>Dark mode</Switch>
  </div>
</template>

Installation

npx @pixelscriptio/tessera add Switch
pnpm exec tessera add Switch
yarn tessera add Switch
bunx @pixelscriptio/tessera add Switch
  • Works with Tessera 0.9.0 or newer.
  1. Copy and paste the code into your project

    Put it in system/components.

    system/components/Switch.tess
    <contract>
    {
      "name": "Switch",
      "role": "switch",
      "description": "A setting that is on or off, and takes effect at once. Press it, or use Space or Enter.",
      "since": "0.5.0",
      "parts": ["root", "track", "thumb", "label"],
      "variants": {
        "size": ["md", "sm"] // first value is the default
      },
      "states": ["hover", "focus-visible", "checked", "disabled"],
      "behavior": "toggle",
      "slots": {
        "default": "the label"
      },
      "events": {
        "toggle": "fired with { checked, source } when the person changes it; the checked prop sets it from outside"
      },
      "preview": {
        "slots": {
          "default": "Notifications"
        }
      }
    }
    </contract>
    
    <script>
    const { checked, disabled } = props({ checked: false, disabled: false })
    
    // The component owns the state, starts from the prop and follows it when it changes.
    const on = state(checked.value)
    effect(() => {
      on.value = checked.value
    })
    
    function change(detail) {
      on.value = detail.checked
      emit('toggle', detail)
    }
    </script>
    
    <template>
      <button
        class="root"
        type="button"
        role="switch"
        :aria-checked="on ? 'true' : 'false'"
        :disabled="disabled"
        @toggle="change($event)"
        >
        <span class="track"><span class="thumb"></span></span>
        <span class="label"><slot /></span>
      </button>
    </template>
    
    <style>
    .root {
      display: inline-flex;
      align-items: center;
      gap: $space.3;
      padding: $space.1 0;
      border: 0;
      background: transparent;
      color: $color.text.default;
      font-family: var(--t-font-family-sans);
      font-size: $font.size.sm;
      cursor: pointer;
    
      &:focus-visible {
        outline: 2px solid $color.focus.ring;
        outline-offset: 2px;
      }
    
      &:disabled {
        opacity: $opacity.disabled;
        cursor: not-allowed;
      }
    
      &[data-size='sm'] {
        font-size: $font.size.sm;
      }
    }
    
    .track {
      display: inline-flex;
      align-items: center;
      box-sizing: content-box;
      width: $space.8;
      height: $space.5;
      padding: 0 $space.0-5;
      border: 1px solid $color.border.strong;
      border-radius: $radius.full;
      background: $color.surface.hover;
      transition-duration: $motion.duration.fast;
    }
    
    .thumb {
      width: $space.4;
      height: $space.4;
      border-radius: $radius.full;
      background: $color.text.muted;
      transition-duration: $motion.duration.fast;
    }
    
    .root[data-size='sm'] .track {
      width: $space.6;
      height: $space.4;
    }
    
    .root[data-size='sm'] .thumb {
      width: $space.3;
      height: $space.3;
    }
    
    .root:hover:not(:disabled) .track {
      border-color: $color.text.default;
    }
    
    .root[aria-checked='true'] .track {
      border-color: $color.action.primary.bg;
      background: $color.action.primary.bg;
    }
    
    .root[aria-checked='true'] .thumb {
      background: $color.action.primary.fg;
      transform: translateX($space.3);
    }
    
    .root[data-size='sm'][aria-checked='true'] .thumb {
      transform: translateX($space.3);
    }
    
    .label:empty {
      display: none;
    }
    </style>
    
  2. Check your design tokens

    Your design system needs these. tessera add reports the ones that are missing; by hand, run npx @pixelscriptio/tessera check.

    color.action.primary.bg color.action.primary.fg color.border.strong color.focus.ring color.surface.hover color.text.default color.text.muted font.size.sm motion.duration.fast opacity.disabled radius.full space.0-5 space.1 space.3 space.4 space.5 space.6 space.8

Usage

Using Switch

Use a switch for a setting that takes effect at once, such as "Dark mode". For a choice that is saved with a form, use a Checkbox.

  • The label names the setting, and does not change when it is on: "Notifications", not "Turn notifications on".
  • It starts off. Set checked to start it on, or to change it from outside.
  • The toggle event says where it is going: { checked, source }.
  • Space and Enter both toggle it.

The preview needs JavaScript. The code is in the Code tab.

Switch-0.tess
<template>
  <div>
    <Switch>Notifications</Switch>
    <Switch checked>Dark mode</Switch>
  </div>
</template>

API reference

Variants

VariantValues
sizemd default sm

Slots

SlotContent
defaultthe label

Events

EventWhen
togglefired with { checked, source } when the person changes it; the checked prop sets it from outside

Anatomy

Roleswitch
Behaviortoggle
Partsroot track thumb label
Stateshover focus-visible checked disabled
Since0.5.0

Design tokens it uses

Your design system needs these. tessera add checks that it has them, and can add the ones it lacks.

color.action.primary.bg color.action.primary.fg color.border.strong color.focus.ring color.surface.hover color.text.default color.text.muted font.size.sm motion.duration.fast opacity.disabled radius.full space.0-5 space.1 space.3 space.4 space.5 space.6 space.8

Checksum

835aadba60256765642688a06ba48c0a56109325fabc9fea12a98366b0cd5aeb