GodUIGodUI
129Follow on X

Anatomy of the GodUI Input

Why the input's focus ring snaps instead of fading, and where to get a ring that fades in on the GPU.

01/02Why the ring snaps
tsx
// shadcn"… transition-[color,box-shadow] …focus-visible:ring-[3px] focus-visible:ring-ring/50"// GodUI: same ring, no transition"… focus-visible:border-ringfocus-visible:ring-[3px] focus-visible:ring-ring/50"
01

Why the ring snaps

shadcn draws the focus ring as a 3px box-shadow and transitions it. A shadow can't move to the compositor: every frame of that fade repaints the field.

The GPU way to fade something in is to give it its own layer and change that layer's opacity. On a <div> that layer is a ::before. An <input> is a replaced element, though, and replaced elements don't get ::before or ::after. The other option, wrapping the input in a <div>, would move your className, ref and layout classes off the element you put them on.

So the Input keeps shadcn's ring and drops the transition. In the scene, the field on the left is a real Input; its ring snaps on and off. The one on the right is an Input Group, whose ring sits on a ::before and fades in (slowed down here).

02/02Result
02

The result

Tab into the field or click it. The border and ring change in the same frame. For a ring that fades in, wrap the field in Input Group.

What's animated

InteractionKeyframe / mechanismPropertiesEasingDuration
Focus ringsnaps (box-shadow, no transition)nonenonenone
Border, invalid ringsnapnonenonenone

Why it snaps: the ring is a box-shadow on the input itself, and box-shadow can only be animated by repainting. A GPU fade needs a separate layer whose opacity changes, and <input> is a replaced element, so it can't have a ::before. A wrapper <div> would give it one, but then className, ref and every layout class at your call sites would land on the wrong element. The snap keeps the input a drop-in replacement. When you want the ring to fade in, wrap the field in Input Group.

Why GPU-only

A box-shadow transition repaints the input on every frame of the fade. GodUI's input never animates, so focusing it costs one repaint.

Reduced motion

Nothing to reduce: the ring already appears in a single frame.

Replacing shadcn

npx shadcn add @godui/input overwrites components/ui/input.tsx. Exports, props and the data-slot attribute match shadcn/ui new-york-v4, so existing imports keep working. The only class removed is transition-[color,box-shadow].