Anatomy of the GodUI Radio Group
A dot that grows into its ring when the selection lands on it, and a press that makes a 16px target feel physical.
↓Scroll to step through it
The dot grows
Radix mounts a radio's indicator only while that radio is selected, so a
keyframe on it runs exactly when the selection lands there. The keyframe is
gated on the item's data-animate, which is set only once its checked state
changes, so the option that's selected on first paint shows its dot without
popping (the same rule as Checkbox). It also requires the dot's own
data-state="checked", so the dot being deselected leaves at once instead of
re-popping on its way out.
The dot starts at 30% of its size, deeper than the checkbox's 50%, because
it has more room to grow inside the ring before it reads as "filled". The
animation sits on the indicator <span>, not the <svg>, so Chrome can
composite it.
Keyboard moves it
With the keyboard, arrow keys move both focus and selection, so each newly selected dot grows in while the previous one disappears. Nothing has to coordinate the two: each radio's indicator mounts and unmounts on its own.
With a pointer, the ring presses to 90% on the bouncy spring while it's held.
shadcn's transition-[color,box-shadow] is gone, so border and focus-ring
changes snap instead of repainting over 150ms.
The result
Click an option, or Tab into the group and use the arrow keys.
- scale
- 0.3 → 1
- opacity
- 0 → 1
<RadioGroupPrimitive.Indicatordata-slot="radio-group-indicator"className="relative flex items-center justify-center [--godui-enter-scale:0.3] group-data-[animate=true]/radio-group-item:data-[state=checked]:animate-godui-fade-scale-in"><CircleIcon className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-primary" /></RadioGroupPrimitive.Indicator>Anatomy of the GodUI Radio Group
A dot that grows into its ring when the selection lands on it, and a press that makes a 16px target feel physical.
- scale
- 0.3 → 1
- opacity
- 0 → 1
<RadioGroupPrimitive.Indicatordata-slot="radio-group-indicator"className="relative flex items-center justify-center [--godui-enter-scale:0.3] group-data-[animate=true]/radio-group-item:data-[state=checked]:animate-godui-fade-scale-in"><CircleIcon className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-primary" /></RadioGroupPrimitive.Indicator>The dot grows
Radix mounts a radio's indicator only while that radio is selected, so a
keyframe on it runs exactly when the selection lands there. The keyframe is
gated on the item's data-animate, which is set only once its checked state
changes, so the option that's selected on first paint shows its dot without
popping (the same rule as Checkbox). It also requires the dot's own
data-state="checked", so the dot being deselected leaves at once instead of
re-popping on its way out.
The dot starts at 30% of its size, deeper than the checkbox's 50%, because
it has more room to grow inside the ring before it reads as "filled". The
animation sits on the indicator <span>, not the <svg>, so Chrome can
composite it.
"aspect-square size-4 shrink-0 rounded-full border border-input text-primary shadow-xs outline-nonetransition-[scale] duration-(--godui-duration-base) ease-spring-bouncyactive:scale-[0.9] motion-reduce:transition-none …"Keyboard moves it
With the keyboard, arrow keys move both focus and selection, so each newly selected dot grows in while the previous one disappears. Nothing has to coordinate the two: each radio's indicator mounts and unmounts on its own.
With a pointer, the ring presses to 90% on the bouncy spring while it's held.
shadcn's transition-[color,box-shadow] is gone, so border and focus-ring
changes snap instead of repainting over 150ms.
The result
Click an option, or Tab into the group and use the arrow keys.
What's animated
| Interaction | Keyframe / mechanism | Properties | Easing | Duration |
|---|---|---|---|---|
| Select | godui-fade-scale-in from 30% | scale, opacity | ease-spring-snappy | 260ms |
| Press | transition on active:scale-[0.9] | scale | ease-spring-bouncy | 260ms |
| Ring color, focus ring | snaps | none | none | none |
Why GPU-only
The dot animates scale and opacity on its wrapper, and the press animates
scale. Nothing repaints while it moves.
Reduced motion
--godui-motion: 0 turns the grow into a 150ms fade, and
motion-reduce:transition-none makes the press instant.
Replacing shadcn
npx shadcn add @godui/radio-group overwrites components/ui/radio-group.tsx.
Exports, props and data-slot attributes match shadcn/ui new-york-v4, so
existing imports keep working. It also installs godui-motion (easings,
keyframes, the FLIP hook) into your project.