# Radio Guides

## Patterns

### Simple Choice Group

When to use:

- A small visible set of mutually exclusive options belongs in a form.

Composition notes:

- Keep all related radios under the same `name`.
- Wrap control and label text together so the full row stays clickable.

Trade-offs:

- Strong native form semantics.
- Weaker fit for dense button-like choice UIs.

```tsx
import { Fieldset, FieldsetLegend, Radio } from '@vega-ui/react';

const methods = [
  { value: 'standard', label: 'Standard · 5–7 business days · Free' },
  { value: 'express', label: 'Express · 2–3 business days · $9.90' },
  { value: 'overnight', label: 'Overnight · next business day · $24.00' },
];

export default function LabeledRadioOption() {
  return (
    <Fieldset>
      <FieldsetLegend style={{ marginBlockEnd: 'var(--spacing-5)' }}>Shipping method</FieldsetLegend>
      <div style={{ display: 'grid', gap: 'var(--spacing-6)' }}>
        {methods.map((method) => (
          <label key={method.value} style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-4)' }}>
            <Radio name='shipping' value={method.value} defaultChecked={method.value === 'standard'} />
            {method.label}
          </label>
        ))}
      </div>
    </Fieldset>
  );
}
```

### Settings Choice Group

When to use:

- Users must choose one persistent mode or policy from a short list.

Composition notes:

- Give the group a surrounding label or fieldset-level context.
- Keep option labels descriptive rather than action-oriented.

Trade-offs:

- Easy to understand in forms.
- Less compact than `SegmentedControl` when all options should read as adjacent buttons.

```tsx
import { Fieldset, FieldsetLegend, Radio, Text } from '@vega-ui/react';

const policies = [
  {
    value: 'anyone',
    label: 'Anyone with the link',
    description: 'People outside your organization can open shared documents without signing in.',
  },
  {
    value: 'organization',
    label: 'People in Acme Corp',
    description: 'Only signed-in members of your organization can open shared documents.',
  },
  {
    value: 'invited',
    label: 'Invited people only',
    description: 'Every document must be shared with each person by email. Existing links stop working.',
  },
];

export default function OptionsWithDescriptions() {
  return (
    <Fieldset style={{ width: '100%' }}>
      <FieldsetLegend style={{ marginBlockEnd: 'var(--spacing-5)' }}>Link sharing</FieldsetLegend>
      <div style={{ display: 'grid', gap: 'var(--spacing-8)' }}>
        {policies.map((policy) => (
          <div key={policy.value} style={{ display: 'flex', alignItems: 'flex-start', gap: 'var(--spacing-4)' }}>
            <Radio
              id={`sharing-${policy.value}`}
              name='sharing'
              value={policy.value}
              defaultChecked={policy.value === 'organization'}
              aria-describedby={`sharing-${policy.value}-description`}
              style={{ flexShrink: 0, marginBlockStart: 'var(--spacing-1)' }}
            />
            <div style={{ display: 'grid', gap: 'var(--spacing-1)' }}>
              <label htmlFor={`sharing-${policy.value}`}>{policy.label}</label>
              <Text id={`sharing-${policy.value}-description`} size={2} style={{ color: 'var(--label-secondary)' }}>
                {policy.description}
              </Text>
            </div>
          </div>
        ))}
      </div>
    </Fieldset>
  );
}
```

### Radio Choice In Overlays

When to use:

- An overlay asks for one required decision before continuing.

Composition notes:

- Keep the list short and scannable.
- Combine with clear dialog or drawer copy so the group meaning is obvious.

Trade-offs:

- Keeps the choice explicit.
- Can feel verbose if there are too many options.

```tsx
import { CSSProperties, FormEvent, useState } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Fieldset,
  FieldsetLegend,
  Heading,
  Icon,
  IconButton,
  Radio,
  Text,
} from '@vega-ui/react';
import { XIcon } from '@vega-ui/icons';

const environments = [
  { value: 'preview', label: 'Preview' },
  { value: 'staging', label: 'Staging' },
  { value: 'production', label: 'Production' },
];

export default function RadioChoiceInOverlays() {
  const [environment, setEnvironment] = useState('staging');

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

  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-6)', width: '100%' }}>
      <Text size={3}>Deploy target: <strong>{environment}</strong></Text>
      <Dialog>
        <DialogTrigger asChild>
          <Button variant='secondary' appearance='ghost'>Change</Button>
        </DialogTrigger>
        <DialogContent aria-labelledby='deploy-target-title'>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-8)' }}>
            <Heading id='deploy-target-title' as='h2' size={4} fontWeight={600}>Deploy target</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)' }}>
            <Fieldset>
              <FieldsetLegend style={{ marginBlockEnd: 'var(--spacing-5)' }}>Environment</FieldsetLegend>
              <div style={{ display: 'grid', gap: 'var(--spacing-6)' }}>
                {environments.map((item) => (
                  <label key={item.value} style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-4)' }}>
                    <Radio name='environment' value={item.value} defaultChecked={item.value === environment} />
                    {item.label}
                  </label>
                ))}
              </div>
            </Fieldset>
            <Button type='submit'>Save</Button>
          </form>
        </DialogContent>
      </Dialog>
    </div>
  );
}
```

## Comparison

### Quick Decision Rule

Use `Radio` when exactly one option from a small visible set must be chosen.

### `Radio` vs `Checkbox`

Use `Radio` when:

- exactly one option should be selected

Use `Checkbox` when:

- options can be selected independently or in combination

Main trade-off: radios enforce exclusivity, while checkboxes support independent selection.

### `Radio` vs `Switch`

Use `Radio` when:

- the user is choosing one option from a set

Use `Switch` when:

- the control represents one immediate binary setting

Main trade-off: radios compare alternatives, while switches toggle one setting on or off.

### `Radio` vs `SegmentedControl`

Use `Radio` when:

- a simple visible group is enough
- native form semantics matter more than always-visible button-like presentation

Use `SegmentedControl` when:

- all options should stay visible as adjacent segments with a stronger selected-state treatment

Main trade-off: radios stay simpler and more form-like, while segmented controls are more visually explicit.

### `Radio` vs `CheckboxCard`

Use `Radio` when:

- exclusive options can stay compact and form-like

Use `CheckboxCard` when:

- the options are independent and each one needs richer descriptive content

### Choose This Component When

- One option from a small set must be selected.
- The group belongs naturally in a form.
- Native radio semantics are the right fit.
- the UI should read as exclusive choice rather than an immediate toggle

### Do Not Choose This Component When

- Options can be selected independently.
- The control is a single immediate binary setting.
- The feature needs a denser always-visible segmented UI.

## Troubleshooting

### Several Radios Can Be Checked At Once

#### Symptom

Multiple options appear selected even though the UI is supposed to allow only one choice.

#### Likely Cause

The radios do not share the same native `name`.

#### How To Verify

- Inspect the rendered `name` attribute.
- Check whether all options in the group use the same value.

#### Fix

- Give all related radios the same `name`.
- Keep unrelated groups separated by different names.

### Only The Tiny Control Feels Clickable

#### Symptom

Users miss the label row because only the radio circle feels interactive.

#### Likely Cause

The visible text is not wrapped into the same clickable label structure.

#### How To Verify

- Hover and click on the text next to the control.
- Inspect whether the label wraps both the radio and the text.

#### Fix

- Use a wrapping `label` for the control and its text.
- Keep the full row clickable when possible.

### Radio Was Used For The Wrong Selection Model

#### Symptom

The UI works technically, but users expect to select multiple items or toggle one setting independently.

#### Likely Cause

`Radio` was chosen where `Checkbox` or `Switch` would better match the product model.

#### How To Verify

- Check whether the choice is exclusive, independent, or binary.
- Compare the flow with other selection controls in the system.

#### Fix

- Use `Checkbox` for independent choices.
- Use `Switch` for one immediate binary setting.
