Switch
A 16px knob in an 18px track, drawn 192 ways. Seven axes on one container, and the largest single variant set in the kit.
The knob is 16 inside an 18-tall track, so it sits 1 from the top and 1 from whichever end. At sm it is 12 inside 14. That single pixel is the whole reason the shape reads as a knob in a slot rather than a circle overlapping a bar.
Off is base.input, on is base.primary, and the knob is base.background in both. No border, no shadow, no third tone. Focus adds a base.ring outside; Invalid adds a destructive edge plus the same colour at a fifth as a halo. Disabled just fades.
Type Box wraps the same row in 10 of padding on a radius.lg edge, and fills with base.primary once it is on. That inverts both text lines: the label to base.primary-foreground, the description to colors.muted-foreground-dark so it stays the quieter of the two.
A hundred and ninety-two, not two hundred and fifty-six
Two sizes, two positions, four states, two types, two placements, and two independent visibility switches. Multiplied out that is 256, and the file publishes 192. The missing 64 are every combination where the description is on and the label is off - a supporting line with nothing to support, which is not a thing anyone builds.
Written as three text arrangements rather than two independent booleans, the sum comes out exactly right the first time. That is worth doing on paper before drawing anything: Checkbox and Radio Group land on 120 and 96 by the same reasoning, and all three would have been a third larger if the impossible cell had been drawn anyway.
Six rows, sixteen apart
320 by 332: six instances stacked in a column at a 16 gap. That is the whole component - it adds no heading, no rule, no card. A settings screen is this and a title above it, which is why the group is worth publishing at all: the gap is the decision.
Eight rows at one width
912 by 736, every example 312 wide. Same row eight ways: on, off, disabled, small, with a description, invalid, and as a card in both positions. The width is fixed on purpose - it is what a settings column actually is, and the label wraps rather than the row growing.
One thing the frame does not show and the source does: data-disabled goes on the Field, not on the control. A disabled row should read as disabled all the way across - the control at half strength and the text at seven tenths - because a full-strength label beside a faded switch looks like a rendering fault rather than a state.
The knob travels the other way
This is the one control on which direction changes something real. On means the knob has moved away from where reading starts, so in an RTL layout it rests on the left. The row order flips with dir, and the knob's travel has to flip with it or on and off swap meaning.
Nothing in the file needs a mirrored copy for this either. Flipping the track horizontally sends the knob the other way without touching its one-pixel offset, and the row order comes from dir. But it is worth drawing the distinction: on the Slider and the Sidebar mirroring is only position, and here it changes which end means yes.
Two components, 192 variants
Read straight out of the Penpot file. The axes below are the property names you will see in the variant dropdown.
Using the Switch
Because the description cannot exist without the label. Two sizes times two positions times four states times two types times two placements is 128, and each is drawn three ways: label with description, label alone, control alone. The fourth - a description with nothing to describe - is not a thing anyone builds. Count the text arrangements as three rather than two independent booleans and the sum comes out at 192 first time.
The track is 32 by 18 with a 16 knob, or 24 by 14 with a 12 knob at sm. The knob sits 1 in from the top and 1 in from whichever end, so its travel is the track minus the knob minus 2. That single pixel of clearance is what makes it read as a knob in a slot rather than a circle sitting on a bar.
Two. Off is base.input, on is base.primary, and the knob is base.background in both. Focus adds a base.ring outside the track; Invalid adds a destructive 1px edge plus the same colour at a fifth as a 2px halo. Nothing else is drawn - no border at rest, no shadow, no third tone.
It wraps the same row in 10 of padding on a radius.lg edge, and fills with base.primary once the switch is on. That inverts the text: the label goes to base.primary-foreground and the description to colors.muted-foreground-dark, which keeps the label the louder of the two. Checkbox and Radio Group do the same, so all three siblings read alike.
Because the gap is the decision. It is six instances in a column at 16 apart and nothing else - no heading, no rule, no card. Having it in the file means nobody has to guess whether settings rows sit 12, 16 or 24 apart, and a real settings screen is this plus a title above it.
More than for most components. On means the knob has moved away from where reading starts, so in an RTL layout it should rest on the left rather than the right. The row order comes from dir, but the knob's travel has to flip with it or on and off quietly swap meaning. Mirroring the track horizontally does it without touching the offset - no variant needed.