# SegmentedControl Guides

## Patterns

### Short View Or Mode Selector

When to use:

- Users switch between a few always-visible views or modes.

Composition notes:

- Keep the option set short.
- Use concise labels that stay comparable side by side.

Trade-offs:

- Strong visual comparison.
- Weak fit for long option sets.

```tsx
import {
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
} from '@vega-ui/react';

export default function ViewRangeSelector() {
  return (
    <SegmentedControl name='calendar-view' defaultValue='week' role='radiogroup' aria-label='Calendar view'>
      <SegmentedControlItem value='day'>
        <SegmentedControlItemInput />
        Day
      </SegmentedControlItem>
      <SegmentedControlItem value='week'>
        <SegmentedControlItemInput />
        Week
      </SegmentedControlItem>
      <SegmentedControlItem value='month'>
        <SegmentedControlItemInput />
        Month
      </SegmentedControlItem>
      <SegmentedControlItem value='year'>
        <SegmentedControlItemInput />
        Year
      </SegmentedControlItem>
      <SegmentedControlIndicator />
    </SegmentedControl>
  );
}
```

### Compact Theme Or Density Selector

When to use:

- A settings surface needs a dense exclusive selector with strong visual affordance.

Composition notes:

- Use icon-only segments only when the meaning is already obvious.
- Keep the option count small and balanced.

Trade-offs:

- Compact and expressive.
- More fragile than radio groups when labels or icons are uneven.

```tsx
import {
  Icon,
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
} from '@vega-ui/react';
import { MonitorIcon, MoonIcon, SunIcon } from '@vega-ui/icons';

export default function IconOnlyModeSelector() {
  return (
    <SegmentedControl name='appearance' defaultValue='system' size='sm' role='radiogroup' aria-label='Appearance'>
      <SegmentedControlItem value='light' title='Light'>
        <SegmentedControlItemInput aria-label='Light' />
        <Icon size='sm'><SunIcon /></Icon>
      </SegmentedControlItem>
      <SegmentedControlItem value='system' title='System'>
        <SegmentedControlItemInput aria-label='System' />
        <Icon size='sm'><MonitorIcon /></Icon>
      </SegmentedControlItem>
      <SegmentedControlItem value='dark' title='Dark'>
        <SegmentedControlItemInput aria-label='Dark' />
        <Icon size='sm'><MoonIcon /></Icon>
      </SegmentedControlItem>
      <SegmentedControlIndicator />
    </SegmentedControl>
  );
}
```

### Exclusive Selector Inside Overlays

When to use:

- A dialog or drawer needs one short, always-visible choice group.

Composition notes:

- Keep the control compact enough for the overlay width.
- Avoid mixing too many long labels in constrained layouts.

Trade-offs:

- Works well for short overlay forms.
- Breaks down if the content becomes too wide or too numerous.

```tsx
import { CSSProperties, FormEvent, useState } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Label,
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
  Text,
} from '@vega-ui/react';
import { XIcon } from '@vega-ui/icons';

const densities = [
  { value: 'compact', label: 'Compact' },
  { value: 'default', label: 'Default' },
  { value: 'comfortable', label: 'Comfortable' },
];

export default function ExclusiveSelectorInsideOverlays() {
  const [density, setDensity] = useState('default');

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    const data = new FormData(event.currentTarget);
    setDensity(String(data.get('density')));
  };

  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-6)', width: '100%' }}>
      <Text size={3}>Row density: <strong>{density}</strong></Text>
      <Dialog>
        <DialogTrigger asChild>
          <Button variant='secondary' appearance='ghost'>Display settings</Button>
        </DialogTrigger>
        <DialogContent aria-labelledby='display-settings-title'>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-8)' }}>
            <Heading id='display-settings-title' as='h2' size={4} fontWeight={600}>Display settings</Heading>
            <DialogClose asChild>
              <IconButton
                aria-label='Close'
                variant='secondary'
                appearance='ghost'
                style={{ flexShrink: 0, '--icon-button-size': '24px', '--icon-button-br-ratio': 0.5 } as CSSProperties}
              >
                <Icon size='xs'><XIcon strokeWidth={2.5} /></Icon>
              </IconButton>
            </DialogClose>
          </div>
          <form method='dialog' onSubmit={handleSubmit} style={{ display: 'grid', gap: 'var(--spacing-12)', marginBlockStart: 'var(--spacing-8)' }}>
            <div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
              <Label id='density-label'>Row density</Label>
              <SegmentedControl name='density' defaultValue={density} role='radiogroup' aria-labelledby='density-label'>
                {densities.map((item) => (
                  <SegmentedControlItem key={item.value} value={item.value}>
                    <SegmentedControlItemInput />
                    {item.label}
                  </SegmentedControlItem>
                ))}
                <SegmentedControlIndicator />
              </SegmentedControl>
            </div>
            <Button type='submit'>Apply</Button>
          </form>
        </DialogContent>
      </Dialog>
    </div>
  );
}
```

### With Separators

When to use:

- Segments need a visible boundary between them, for example on a low-contrast surface.

Composition notes:

- Place a vertical `Separator` between the items; it is a sibling, not part of any item.
- Give the root `display: flex` so the separators line up with the items.

Trade-offs:

- Clearer boundaries between options.
- The separators stay visible next to the selected segment.

```tsx
import {
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
  Separator,
} from '@vega-ui/react';

export default function WithSeparators() {
  return (
    <SegmentedControl
      name='sort-order'
      defaultValue='newest'
      role='radiogroup'
      aria-label='Sort by'
      style={{ display: 'flex', width: 'min-content' }}
    >
      <SegmentedControlItem value='newest'>
        <SegmentedControlItemInput />
        Newest
      </SegmentedControlItem>
      <Separator orientation='vertical' style={{ height: '80%' }} />
      <SegmentedControlItem value='popular'>
        <SegmentedControlItemInput />
        Most popular
      </SegmentedControlItem>
      <Separator orientation='vertical' style={{ height: '80%' }} />
      <SegmentedControlItem value='price'>
        <SegmentedControlItemInput />
        Price
      </SegmentedControlItem>
      <SegmentedControlIndicator />
    </SegmentedControl>
  );
}
```

## Comparison

### Quick Decision Rule

Use `SegmentedControl` when a short set of exclusive options should stay visible and comparable.

### `SegmentedControl` vs `Radio`

Use `SegmentedControl` when:

- options should stay visible as adjacent segments
- visual comparison matters

Use `Radio` when:

- a simpler form-like exclusive choice group is enough

Main trade-off: segmented control is more explicit and compact, while radio is simpler and more conventional in forms.

### `SegmentedControl` vs `Select`

Use `SegmentedControl` when:

- the option set is small and should stay visible

Use `Select` when:

- space is constrained or the option list is longer

Main trade-off: segmented control favors visibility, while select favors density and scalability.

### `SegmentedControl` vs `Switch`

Use `SegmentedControl` when:

- the user is choosing one option from several visible states

Use `Switch` when:

- the control is one immediate binary toggle

Main trade-off: segmented control is exclusive choice, while switch is binary state change.

### `SegmentedControl` vs `CheckboxCard`

Use `SegmentedControl` when:

- options should stay short, dense, and mutually exclusive

Use `CheckboxCard` when:

- options are richer, card-like, or independently selectable

### Choose This Component When

- A short exclusive option set should remain visible.
- The user benefits from comparing options side by side.
- The UI should feel denser and more explicit than a radio list.
- the options are short enough to remain visually balanced

### Do Not Choose This Component When

- The option list is long.
- The choice is binary and immediate.
- The flow only needs a simple radio group.

## Troubleshooting

### Indicator Does Not Match The Selected Item

#### Symptom

The highlighted indicator appears offset or sized incorrectly.

#### Likely Cause

The indicator resolves its anchor inside the nearest positioned ancestor. A custom wrapper with `position: relative/absolute` between the root and the items, or overriding the root `position`, breaks the anchor lookup.

#### How To Verify

- Test the control with realistic labels and container width.
- Check for positioned wrappers between `SegmentedControl` and its items.

#### Fix

- Keep items direct children of the root, without positioned wrappers.
- Retest custom widths, separators, and dynamic label changes.

### Control Looks Right But Does Not Behave Like A Radio Group

#### Symptom

The segmented control changes visually, but native form or radio-group behavior is missing.

#### Likely Cause

`SegmentedControlItemInput` was omitted or `name` was not set correctly.

#### How To Verify

- Inspect the rendered inputs.
- Confirm that all items share the same `name`.

#### Fix

- Render `SegmentedControlItemInput` inside each item.
- Keep the required `name` consistent across the group.

### Labels Feel Too Uneven

#### Symptom

The control becomes visually unbalanced because some segments are much longer than others.

#### Likely Cause

Segmented control is being used for labels with very different lengths or too many options.

#### How To Verify

- Compare the visible label lengths side by side.
- Check whether the control still reads as one balanced choice group.

#### Fix

- Shorten labels where possible.
- Switch to `Radio` or `Select` when the option set is no longer a good segmented-control fit.
