Tessera UI

Components

Select

A list of options that opens from a field and takes one, on the browser's own select. Use a Combobox to search a long list.

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

Select-0.tess
<script>
const sizes = [
  { value: 's', label: 'Small' },
  { value: 'm', label: 'Medium' },
  { value: 'l', label: 'Large' },
]
</script>

<template>
  <Select :options="sizes" value="m">
    <span slot="label">Size</span>
  </Select>
</template>

Installation

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

    Put it in system/components.

    system/components/Select.tess
    <contract>
    {
      "name": "Select",
      "role": "generic",
      "description": "A list of options that opens from a field and takes one, on the browser's own select. Use a Combobox to search a long list.",
      "since": "0.5.0",
      "parts": ["root", "label", "select"],
      "variants": {
        "size": ["md", "sm"] // first value is the default
      },
      "states": ["hover", "focus-visible", "disabled"],
      "slots": {
        "label": "the visible label, required"
      },
      "events": {
        "change": "fired with { value, label } when the person chooses an option"
      },
      "preview": {
        "props": {
          "options": [
            {
              "value": "apple",
              "label": "Apple"
            },
            {
              "value": "apricot",
              "label": "Apricot"
            },
            {
              "value": "banana",
              "label": "Banana"
            },
            {
              "value": "blueberry",
              "label": "Blueberry"
            }
          ]
        },
        "slots": {
          "label": "Fruit"
        }
      }
    }
    </contract>
    
    <script>
    const { options, value, disabled } = props({ options: [], value: '', disabled: false })
    
    // The select owns what is chosen, starts from the prop and follows it when it changes.
    const current = state(value.value)
    effect(() => {
      current.value = value.value
    })
    
    function changed(event) {
      const chosen = options.value.find((o) => o.value === event.target.value)
      emit('change', { value: event.target.value, label: chosen ? chosen.label : '' })
    }
    </script>
    
    <template>
      <label class="root">
        <span class="label"><slot name="label" /></span>
        <select class="select" :disabled="disabled" bind:value="current" @change="changed($event)">
          <option
            each="o in options"
            key="o.value"
            :value="o.value"
            :selected="o.value === current"
            >
            {o.label}
          </option>
        </select>
      </label>
    </template>
    
    <style>
    .root {
      display: flex;
      flex-direction: column;
      gap: $space.1;
      color: $color.text.default;
      font-family: var(--t-font-family-sans);
      font-size: $font.size.sm;
    }
    
    .label {
      font-size: $font.size.sm;
      font-weight: $font.weight.bold;
    }
    
    .select {
      padding: $space.2 $space.3;
      border: 1px solid $color.border.strong;
      border-radius: $radius.md;
      background: $color.surface.default;
      color: $color.text.default;
      font: inherit;
      transition-duration: $motion.duration.fast;
    
      &:hover:not(:disabled) {
        border-color: $color.text.default;
      }
    
      &:focus-visible {
        outline: 2px solid $color.focus.ring;
        outline-offset: 2px;
      }
    
      &:disabled {
        opacity: $opacity.disabled;
        cursor: not-allowed;
      }
    }
    
    .root[data-size='sm'] .select {
      padding: $space.1 $space.2;
      font-size: $font.size.sm;
    }
    </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.border.strong color.focus.ring color.surface.default color.text.default font.size.sm font.weight.bold motion.duration.fast opacity.disabled radius.md space.1 space.2 space.3

Usage

Using Select

Use a select to choose one option from a short list, on the browser's own select, so it works the way people already know on every device. For a long list that people search, use a Combobox.

  • The label is required: it is what assistive technology reads.
  • Give it options, a list of { value, label }, and value to start with one chosen.
  • change fires with { value, label } when the person chooses an option.

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

Select-0.tess
<script>
const sizes = [
  { value: 's', label: 'Small' },
  { value: 'm', label: 'Medium' },
  { value: 'l', label: 'Large' },
]
</script>

<template>
  <Select :options="sizes" value="m">
    <span slot="label">Size</span>
  </Select>
</template>

API reference

Variants

VariantValues
sizemd default sm

Slots

SlotContent
labelthe visible label, required

Events

EventWhen
changefired with { value, label } when the person chooses an option

Anatomy

Rolegeneric
Partsroot label select
Stateshover focus-visible 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.border.strong color.focus.ring color.surface.default color.text.default font.size.sm font.weight.bold motion.duration.fast opacity.disabled radius.md space.1 space.2 space.3

Checksum

a2a7e52d99e169896261531b4d6c865c587bcc876a3bce901bd353b56b669210