---
title: Radio
description: Radio lets people choose exactly one option from a visible set. Appetite UI Figma component: four states with a focus ring, tokens and Material 3 notes.
group: Components
source: https://www.appetiteui.com/docs/radio
dateModified: 2026-10-03
---

# Radio

Updated: October 3, 2026.

Radio is a form control for choosing exactly one option from a set.

## Preview

One choice out of a visible set. Every option stays on screen and exactly one is filled. Click one, or tab to the group and use the arrow keys.

Specimen: A live group at Align Left, one selected. row 340, radio 44, content 280 at gap 16.

The whole row is the target: the control is a 44 frame around a 22 disc, so the row clears the minimum even though the visible circle is smaller. Content takes the remaining 280 and wraps inside the row.

A radio never ships alone, because a single one cannot be turned off again, which is why the preview is a group.

Source: Radio, node 211:4275 of the Appetite UI Figma file, read on October 2, 2026.

## Which states does Radio have?

Four: Default, Pressed, Focused and Disabled, crossed with one boolean for selected. The file has no Hover state, so a pointer hover moves these specimens toward Pressed.

Specimen: Default, Pressed, Focused and Disabled, each unselected then selected. 44 target, 22 disc, center 18 or 10.

The disc is 22 and the center circle 18, optically matched to the 24 square of Checkbox so the two sit side by side in a 44 target. The selected dot is 10 and static white in both modes.

Focused is the focusRing/brand effect style, a 2 pt gap in the surface color and then a 2 pt ring in brand/base, drawn on the disc. The unselected disc is bg/sub-200, so an empty radio stays visible on a white screen.

The ring is the 22 disc showing round an 18 circle in bg/white-0. Pressed while unselected turns the disc brand blue.

Disabled while unselected has no center circle, a flat gray disc.

8 variants, State crossed with Active. They are there to be read, so nothing in this frame takes a click.

## Label row

The radio with a title, an optional description, and a side to sit on. The whole row is the tap target. A group of them is how the control ships.

Specimen: Align Left and Align Right. 340 row, 44 radio, 280 of content, gap 16.

Specimen: Show Description off, the same two aligns. title 18/28 Semi Bold, description 14/20 Regular.

Align moves the radio from one end to the other and changes nothing else. Turning the description off drops the 14/20 line, and the 44 target sets the floor for the row height.

The title is 18/28 Semi Bold, so a long option name uses the width fast. Both rows in each frame stay selected on purpose, because they are one variant shown twice.

## Properties

Two axes on the control, four properties on the label row.

| Property | Type | Default |
| --- | --- | --- |
| `State` | Default \| Pressed \| Disabled \| Focused, on Radio | Default |
| `Active` | True \| False, on Radio | False |
| `Align` | Left \| Right, on _Radio Label | Left |
| `Label` | Text, on _Radio Label | Title of radio |
| `Description` | Text, on _Radio Label | Insert the label description here. |
| `Show Description` | Boolean, on _Radio Label | True |

| Set | Variants | Size |
| --- | --- | --- |
| `Radio` | 8, State crossed with Active | 44 x 44 |
| `_Radio Label` | 2, on Align | 340 wide, 48 tall with a description |

There is no group component. A radio group is however many label rows you place, and keeping exactly one Active is your job.

Nothing carries a name or a value, so the wiring lives in code.

## Tokens

Every value the radio reads, resolved through its aliases to the primitive underneath.

| Token | Light | Dark | Applied to |
| --- | --- | --- | --- |
| `bg/sub-200` | #D1D1D6 | #636366 | Unselected disc |
| `brand/base` | #155DFC | #2B7FFF | Selected disc, pressed unselected disc, focus ring |
| `brand/pressed` | #193CB8 | #155DFC | Disc when selected and pressed |
| `bg/white-0` | #FFFFFF | #0E0E0F | Center circle when unselected, and the gap in the focus ring |
| `icon/on-color` | #FFFFFF | #FFFFFF | Dot when selected |
| `bg/soft-100` | #E9E9EA | #48484A | Both disabled discs |
| `fg/disabled-400` | #AEAEB2 | #8E8E93 | Disabled dot |
| `text/strong-950` | #0E0E0F | #FFFFFF | Title |
| `text/sub-700` | #48484A | #C7C7CC | Description |

Selected and pressed, the disc fills with brand/pressed, a step darker than brand/base in Light and in Dark. The center circle is bg/white-0 and opaque, so an unselected radio on any other surface shows a disc of the wrong tone.

## How does Radio differ on iOS and Material 3?

iOS has no radio button, and Material 3 has one with a 48 by 48 target. Appetite UI Radio is one component with a 44 pt target for both.

| Appetite UI | iOS counterpart | Material 3 counterpart | What differs |
| --- | --- | --- | --- |
| Radio | None. iOS shows one choice among several as an inline picker or a list row with a checkmark. | M3 / Radio, 48 by 48, Selected by Is Enabled | Appetite UI draws a 22 disc in a 44 target and adds Pressed and Focused. The Material 3 set has no Pressed, Hovered or Focused variant. |

The minimum touch target is 44 pt on iOS and 48 dp on Material 3, set by `platform/min-touch-target` in the Platform collection.

The 44 frame meets the iOS minimum. On Android, add 4 dp of hit area in code.

## Guidelines

- Do: Exactly one filled, and every option on screen. If the user cannot see all the choices at once, the control is a Dropdown or a Picker, not this.

- Avoid: Two filled is a Checkbox wearing the wrong shape. Round means one, square means any number, and people read that before they read the label.

Use it for two to five options that fit on screen together and deserve to be compared. One option is not a choice, and past five a list or a picker wins.

Always start with one selected, and never use a radio for an action.
