Components/Radio Group

Radio Group

Ninety-six variants of a 16px circle: chosen or not, four states, plain or boxed, control on either side, and three amounts of text beside it.

Radio Button · Components frameLive in the browser · drawn to match the file
Active
State
Type
Control
Shows
Radio Button Text This is a radio description.
Selected · Default · Default · Start189 × 37 in the file
Ninety-six, and why it is not more

Two selections times four states times two types times two placements is thirty-two, and each of those is drawn three ways: with a description, with only a label, with neither. Four ways would be thirty-two more, but a description without a label is not a thing, so it was never drawn.

A circle that is a square

The control is a 16 board on radius.md, which is 8 - exactly half its side, so it renders as a circle without needing radius.full. Inside sits an 8px ellipse in base.primary-foreground, shown only when the option is chosen.

Box is a card, not a skin

Type Box wraps the same row in 10 of padding and a base.input edge on radius.lg, and a selected box fills with base.primary - so the label and description have to invert with it. That inversion is one of the four things the file had wrong, and all four are fixed.

The second component

A group is the rule that only one wins

189 by 262 in the file: six buttons stacked 8 apart, and that spacing is the whole component. The behaviour is not drawable - picking one has to clear the rest - so here it is live, which is the only place it can be shown.

Radio GroupClick one - the others clear
Four ways to use it

The same control, four densities

All four run on the same 16px control and the same 8 gap; what changes is how much sits beside it. Every one of them is clickable, because a radio group that cannot clear its other options is not showing you anything.

Example

The last example uses Control Placement End, which is the axis worth knowing about: in a settings list the labels are the thing you scan and the controls line up on the right, while in a form the control comes first because it is what you are aiming at. Same ninety-six variants, one property apart.

Right to left

Control Placement is a side, and the side moves

Start means the control comes before the label in reading order, so under dir=rtl it sits on the right. The variant name stays true and the pixels move. The circle is symmetrical and the dot inside it is centred, so the control itself needs no mirroring at all.

dir=rtlLive in the browser · drawn to match the file
Direction
control on the left · dot stays centred

Ninety-six variants would become a hundred and ninety-two for something dir already does, so no mirrored copies exist and none should. Checkbox works exactly this way; Switch is the one sibling where direction changes more than position, because its knob has to travel the other way or on and off swap meaning.

In the file

Two components, ninety-six variants

Read straight out of the Penpot file. The axes below are the property names you will see in the variant dropdown.

Radio ButtonActive × State × Type × Control Placement × Show Label × Show DescriptionSelected · Not × Default · Focus · Disabled · Invalid × Default · Box × Start · End × three text combinations96
Radio Groupnone189 × 262 - six buttons stacked 8 apart1

Using the Radio Group

Because one text combination cannot exist. Two selections times four states times two types times two placements is thirty-two, and each is drawn three ways: label with description, label alone, control alone. The fourth - a description with no label - is not a thing anyone builds, so it was never drawn, and thirty-two of the possible variants disappear with it.

Because at 16 across, radius.md is 8, which is exactly half the side - the corners meet and it renders as a circle without reaching for radius.full. Inside it sits an 8px ellipse in base.primary-foreground, present only when the option is chosen; the unchosen control is base.background on a base.input edge with nothing in the middle.

It wraps the same row in 10 of padding and a radius.lg edge, turning a list item into a card you can click anywhere on. A selected box fills with base.primary, which means the label and the description have to invert with it - that inversion is what the file had wrong.

In a settings list, where the labels are what you scan and the controls should line up on one edge. In a form the control comes first, because it is the thing you are aiming at and the label explains it. Same variants, one property apart - the last usage example on this page shows both in one screen.

colors.muted-foreground-dark, at #a1a1a1 - quieter than the label, and 6.9:1 on the near-black card. Making it the same white as the label passes contrast easily but flattens the hierarchy: two lines shouting equally in a card whose whole job is to say one option is picked. Checkbox and Switch read the same way, so all three siblings agree. The destructive card is the exception and stays white on both lines, because red at that lightness leaves no room for a third step.

Forms · 96 variants