Segmented Control

Choose one option from a short visible set on a shared track. A raised selected segment uses the current theme surfaces. Native radio inputs supply keyboard navigation, validation, submission and reset. Supply name for behavior before upgrade. The select and change events follow Radio Group; value updates replace the selection without reporting a user edit.

One visible choice

Choose one status from a shared track. Native radio buttons supply arrow-key navigation and form submission.

Status

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-segmented-control/ui-segmented-control.html"
>

<ui-segmented-control
  label="Status"
  name="status"
  value="active"
  items="[{value: &quot;active&quot;, label: &quot;Active&quot;}, {value: &quot;archived&quot;, label: &quot;Archived&quot;}]"
></ui-segmented-control>
<script setup>
  import { UiSegmentedControl } from "@threadlabs/looma";
</script>

<template>
  <UiSegmentedControl
    label="Status"
    name="status"
    value="active"
    :items="[{ value: 'active', label: 'Active' }, { value: 'archived', label: 'Archived' }]"
  />
</template>
import { UiSegmentedControl } from "@threadlabs/looma";

export default function Example() {
  return <UiSegmentedControl
    label="Status"
    name="status"
    value="active"
    items={[{"value":"active","label":"Active"},{"value":"archived","label":"Archived"}]}
  />;
}
/** @jsxImportSource preact */
import { UiSegmentedControl } from "@threadlabs/looma";

export default function Example() {
  return <UiSegmentedControl
    label="Status"
    name="status"
    value="active"
    items={[{"value":"active","label":"Active"},{"value":"archived","label":"Archived"}]}
  />;
}
<script>
  import { UiSegmentedControl } from "@threadlabs/looma";
</script>

<UiSegmentedControl
  label="Status"
  name="status"
  value="active"
  items={[{"value":"active","label":"Active"},{"value":"archived","label":"Archived"}]}
/>
import { Component } from "@angular/core";
import { UiSegmentedControl } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiSegmentedControl],
  template: `
    <fieldset
      ui-segmented-control
      [label]="'Status'"
      [name]="'status'"
      [value]="'active'"
      [items]="[{ value: 'active', label: 'Active' }, { value: 'archived', label: 'Archived' }]"
    ></fieldset>
  `,
})
export default class Example {}

Sizes and unavailable choices

Small and large controls share field sizes. An unavailable option stays visible, while disabling the group prevents every choice.

View
View

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-segmented-control/ui-segmented-control.html"
>

<ui-stack>
  <ui-segmented-control
    size="sm"
    label="View"
    name="small-view"
    value="list"
    items="[{value: &quot;list&quot;, label: &quot;List&quot;}, {value: &quot;grid&quot;, label: &quot;Grid&quot;}, {value: &quot;board&quot;, label: &quot;Board&quot;, disabled: true}]"
  ></ui-segmented-control>
  <ui-segmented-control
    size="lg"
    label="View"
    name="large-view"
    value="grid"
    disabled
    items="[{value: &quot;list&quot;, label: &quot;List&quot;}, {value: &quot;grid&quot;, label: &quot;Grid&quot;}]"
  ></ui-segmented-control>
</ui-stack>
<script setup>
  import { UiSegmentedControl, UiStack } from "@threadlabs/looma";
</script>

<template>
  <UiStack>
    <UiSegmentedControl
      size="sm"
      label="View"
      name="small-view"
      value="list"
      :items="[{ value: 'list', label: 'List' }, { value: 'grid', label: 'Grid' }, { value: 'board', label: 'Board', disabled: true }]"
    />
    <UiSegmentedControl
      size="lg"
      label="View"
      name="large-view"
      value="grid"
      :disabled="true"
      :items="[{ value: 'list', label: 'List' }, { value: 'grid', label: 'Grid' }]"
    />
  </UiStack>
</template>
import { UiSegmentedControl, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack>
    <UiSegmentedControl
      size="sm"
      label="View"
      name="small-view"
      value="list"
      items={[{"value":"list","label":"List"},{"value":"grid","label":"Grid"},{"value":"board","label":"Board","disabled":true}]}
    />
    <UiSegmentedControl
      size="lg"
      label="View"
      name="large-view"
      value="grid"
      disabled
      items={[{"value":"list","label":"List"},{"value":"grid","label":"Grid"}]}
    />
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiSegmentedControl, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack>
    <UiSegmentedControl
      size="sm"
      label="View"
      name="small-view"
      value="list"
      items={[{"value":"list","label":"List"},{"value":"grid","label":"Grid"},{"value":"board","label":"Board","disabled":true}]}
    />
    <UiSegmentedControl
      size="lg"
      label="View"
      name="large-view"
      value="grid"
      disabled
      items={[{"value":"list","label":"List"},{"value":"grid","label":"Grid"}]}
    />
  </UiStack>;
}
<script>
  import { UiSegmentedControl, UiStack } from "@threadlabs/looma";
</script>

<UiStack>
  <UiSegmentedControl
    size="sm"
    label="View"
    name="small-view"
    value="list"
    items={[{"value":"list","label":"List"},{"value":"grid","label":"Grid"},{"value":"board","label":"Board","disabled":true}]}
  />
  <UiSegmentedControl
    size="lg"
    label="View"
    name="large-view"
    value="grid"
    disabled
    items={[{"value":"list","label":"List"},{"value":"grid","label":"Grid"}]}
  />
</UiStack>
import { Component } from "@angular/core";
import { UiSegmentedControl, UiStack } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiSegmentedControl],
  template: `
    <div
      ui-stack
    >
      <fieldset
        ui-segmented-control
        [size]="'sm'"
        [label]="'View'"
        [name]="'small-view'"
        [value]="'list'"
        [items]="[{ value: 'list', label: 'List' }, { value: 'grid', label: 'Grid' }, { value: 'board', label: 'Board', disabled: true }]"
      ></fieldset>
      <fieldset
        ui-segmented-control
        [size]="'lg'"
        [label]="'View'"
        [name]="'large-view'"
        [value]="'grid'"
        [disabled]="true"
        [items]="[{ value: 'list', label: 'List' }, { value: 'grid', label: 'Grid' }]"
      ></fieldset>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: fieldset

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
disabledDisable the group and exclude its value from form submission. Individually disabled choices retain their own state.disabledbooleanfalse—
formAssociate the native radios with this form ID. Omit it to use their containing form.formstring——
invalidExpose validation failure on the group and its visible boundary.invalidbooleanfalse—
itemsA short set of mutually exclusive choices. Values must be unique; labels remain visible.itemslist(object)
  • [].value: string (required)
  • [].label: string (required)
  • [].disabled: boolean
——
labelThe question answered by the choices, used as the group's legend and accessible name.labelstringOptions—
label-visibilityHide the legend visually only when the surrounding content already names the choice.labelVisibilitykeywordvisiblevisible, sr-only
nameThe native radio name used for form submission. Supply a unique name for each group so it also works before JavaScript connects.namestring—
requiredRequire one native radio selection for form validation.requiredbooleanfalse—
sizeMatch the height and type size of nearby form controls.sizekeywordmdsm, md, lg
valueThe selected value, initially or when changed externally. A native form reset returns to this value. Empty selects none.valuestring—

Framework props

NameTypeDefaultAllowed values
disabledbooleanfalse—
formstring——
invalidbooleanfalse—
itemsreadonly ({ readonly value: string; readonly label: string; readonly disabled?: boolean | null })[]
  • [].value: string (required)
  • [].label: string (required)
  • [].disabled: boolean
——
labelstringOptions—
labelVisibility"visible" | "sr-only"visiblevisible, sr-only
namestring—
requiredbooleanfalse—
size"sm" | "md" | "lg"mdsm, md, lg
valuestring—

Events

NameDetailDescription
changeobject
  • checked: boolean (required)
  • value: string (required)
  • trigger: keyword (required)
Emitted when a toggleable control changes checked state.
selectobject
  • value: string (required)
  • previousValue: string (required)
  • trigger: keyword (required)
Emitted when a selectable option becomes active.

Slots

No slots.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--ui-segmented-control-border—var(--ui-control-border)
--ui-segmented-control-radiusvar(--ui-field-radius, var(--ui-radius-md))—
--ui-segmented-control-selected-border—var(--ui-border)
--ui-segmented-control-selected-surface—var(--ui-surface-raised)
--ui-segmented-control-track—var(--ui-surface-sunken)

Shared tokens

TokenFallback
--ui-border—
--ui-border-width—
--ui-control-border—
--ui-control-size-lg—
--ui-control-size-md—
--ui-control-size-sm—
--ui-danger-solid—
--ui-disabled-border—
--ui-disabled-surface—
--ui-disabled-text—
--ui-field-dangervar(--ui-danger-solid)
--ui-field-radiusvar(--ui-radius-md)
--ui-focus-ring—
--ui-focus-width—
--ui-font-medium—
--ui-font-size-lg—
--ui-font-size-md—
--ui-font-size-sm—
--ui-line-height-tight—
--ui-radius-md—
--ui-shadow-sm—
--ui-space-1—
--ui-space-2—
--ui-space-3—
--ui-space-4—
--ui-surface-raised—
--ui-surface-sunken—
--ui-text-primary—