Skip to main content
Browse docs

Marwes documentation

Segmented Control

Single-select segmented toggle family with radiogroup semantics, keyboard navigation, and three visual variants (default, inverse, pill).

What this family solves

Single-select segmented toggle family with radiogroup semantics, keyboard navigation, and three visual variants (default, inverse, pill).

Use when

  • The UI needs a compact single-select toggle between 2–4 options with no associated panel content.
  • Theme switching, view density controls, framework selectors, or similar compact option sets.
  • The team wants React, Vue, and Svelte segmented behavior to stay aligned through shared core recipes.

Avoid when

  • The selection controls associated panel content — use TabGroup instead.
  • There are more than 4 options — consider a dropdown or radio group.
  • A binary on/off toggle is more appropriate — use SwitchField instead.
  • The options need rich content or descriptions — use a radio group field.

Public imports

Choose an adapter in the verified examples. The inventory below reflects each framework package exactly.

React — @marwes-ui/react

Component

SegmentedControlField

Types

SegmentedControlFieldProps, SegmentedControlItem

Vue — @marwes-ui/vue

Component

SegmentedControlField

Types

SegmentedControlFieldProps, SegmentedControlItem

Svelte — @marwes-ui/svelte

Component

SegmentedControlField

Types

SegmentedControlFieldProps, SegmentedControlItem

Verified minimal examples

These examples come from the same fixtures verified by the repository checks.

React

import { Icon, IconName, SegmentedControlField } from "@marwes-ui/react"
import { useState } from "react"

export function Example() {
  const [density, setDensity] = useState("compact")
  const [mode, setMode] = useState("compact")
  const [theme, setTheme] = useState("light")

  return (
    <>
      <SegmentedControlField
        label="View density"
        segmentedControl={{
          items: [
            { value: "compact", label: "Compact" },
            { value: "wide", label: "Wide" },
          ],
          value: density,
          onValueChange: setDensity,
          variant: "inverse",
          fullWidth: true,
        }}
      />
      <SegmentedControlField
        label="View mode"
        segmentedControl={{
          items: [
            {
              value: "compact",
              icon: <Icon name={IconName.Star} decorative size={12} />,
              label: "Compact",
            },
            {
              value: "wide",
              icon: <Icon name={IconName.Settings} decorative size={12} />,
              label: "Wide",
            },
            {
              value: "rich",
              icon: <Icon name={IconName.Star} decorative size={12} />,
              label: "Rich",
            },
          ],
          value: mode,
          onValueChange: setMode,
          variant: "inverse",
          fullWidth: true,
        }}
      />
      <SegmentedControlField
        label="Theme"
        segmentedControl={{
          items: [
            {
              value: "light",
              icon: <Icon name={IconName.Sun} decorative size={12} />,
              ariaLabel: "Light",
            },
            {
              value: "dark",
              icon: <Icon name={IconName.Moon} decorative size={12} />,
              ariaLabel: "Dark",
            },
          ],
          value: theme,
          onValueChange: setTheme,
          variant: "inverse",
          size: "sm",
        }}
      />
    </>
  )
}

Vue

<script setup lang="ts">
import { h } from "vue"
import { Icon, IconName, SegmentedControlField } from "@marwes-ui/vue"

const modeItems = [
  { value: "compact", icon: h(Icon, { name: IconName.Star, decorative: true, size: 12 }), label: "Compact" },
  { value: "wide", icon: h(Icon, { name: IconName.Settings, decorative: true, size: 12 }), label: "Wide" },
  { value: "rich", icon: h(Icon, { name: IconName.Star, decorative: true, size: 12 }), label: "Rich" },
]

const themeItems = [
  { value: "light", icon: h(Icon, { name: IconName.Sun, decorative: true, size: 12 }), ariaLabel: "Light" },
  { value: "dark", icon: h(Icon, { name: IconName.Moon, decorative: true, size: 12 }), ariaLabel: "Dark" },
]
</script>

<template>
  <SegmentedControlField
    label="View density"
    :segmented-control="{ items: [
      { value: 'compact', label: 'Compact' },
      { value: 'wide', label: 'Wide' },
    ], variant: 'inverse', fullWidth: true }"
  />
  <SegmentedControlField label="View mode" :segmented-control="{ items: modeItems, variant: 'inverse', fullWidth: true }" />
  <SegmentedControlField label="Theme" :segmented-control="{ items: themeItems, variant: 'inverse', size: 'sm' }" />
</template>

Svelte

<script lang="ts">
  import { SegmentedControlField } from "@marwes-ui/svelte"

  let density = $state("compact")
  let mode = $state("compact")
  let theme = $state("light")
</script>

<SegmentedControlField
  label="View density"
  segmentedControl={{ items: [
    { value: "compact", label: "Compact" },
    { value: "wide", label: "Wide" },
  ], value: density, onvaluechange: (value) => (density = value), variant: "inverse", fullWidth: true }}
/>
<SegmentedControlField
  label="View mode"
  segmentedControl={{ items: [
    { value: "compact", label: "Compact" },
    { value: "wide", label: "Wide" },
    { value: "rich", label: "Rich" },
  ], value: mode, onvaluechange: (value) => (mode = value), variant: "inverse", fullWidth: true }}
/>
<SegmentedControlField
  label="Theme"
  segmentedControl={{ items: [
    { value: "light", ariaLabel: "Light" },
    { value: "dark", ariaLabel: "Dark" },
  ], value: theme, onvaluechange: (value) => (theme = value), variant: "inverse", size: "sm" }}
/>

Accessibility requirements

  • Icon-only segment naming: Give every icon-only segment a truthful ariaLabel since no visible text label exists.
  • Keyboard segment navigation: Verify arrow-key navigation moves focus between segments the same way pointer input does.
  • Connected field wiring: Use the public Field component so labels, helper text, errors, and invalid state stay connected.
  • Manual review boundary: Real browser and assistive-technology validation for radio-group announcement patterns.
  • Manual review 2: Focus visibility and roving tabindex feel across screen readers.
  • Manual review 3: High-contrast mode appearance of the active pill indicator.

Theming

Segmented Control uses Marwes preset CSS and theme variables. Customize it through the theme passed to MarwesProvider; do not add a second component stylesheet. See the theming guide for configuration and token details.

Resources