Spinner

A 270 degree arc in five sizes, and a second axis that exists only because Penpot cannot spin anything.

One component, twenty variants

Using the Spinner

Why does a spinner need twenty variants?
It does not, in code - the source is one line, an arc with animate-spin. Five of the twenty are the sizes you actually place. The other axis is four quarter-turn positions of that spin, recorded because a design file cannot animate. It is the honest way to put a moving thing in a static frame, and it doubles as a keyframe list for whoever builds it.
Which Spin Degree should I place?
Whichever sits best in the mockup - they are the same drawing turned a quarter each time, and no user will ever see a still one. The gap pointing away from the text beside it usually reads calmest. If you are handing the file to a developer, the degree is noise: they will write animate-spin and the position becomes whatever the page load happens to catch.
Why does the stroke change with the size?
Because it is always a twelfth of the box: 1 at 12, 1.333 at 16, 2 at 24, 2.667 at 32. That is what keeps a small spinner from looking like a hairline and a large one from looking like a doughnut. It also means resizing an instance is not the same as picking a size - the stroke does not follow, so choose the variant first.
Why 270 degrees and not a full ring?
Because the gap is what says it is moving. A closed circle reads as a border or a badge, and in a still frame there is nothing left to suggest rotation. Three quarters with round caps is enough arc to look like a shape and enough gap to look unfinished, which is the whole trick.
Is it Tabler or lucide?
Tabler's loader-2, like every other glyph in the kit. shadcn uses lucide's LoaderIcon, which is twelve separate ticks around a circle rather than one arc. Both spin; the arc reads smoother under a linear animation, and matching the kit's own icon set matters more here than matching the import line.
Where does it actually go?
Inside other components, and it owns none of them. Button carries its own Loading state, so the six loading buttons in the usage frame are Button instances rather than a spinner dropped beside a label. Badge and Input Group take it inline. Empty puts it in the media slot, which it got by swapping the media component - the slot was already the right size, so nothing needed moving.
Does it need an RTL version?
No. A rotating circle has no handedness, so the glyph is identical in both directions; what changes is which end of the row it sits on, and that is flex order. Set dir on the container and the spinner follows the text it belongs to.