Anatomy of the GodUI Tooltip
Radix has three open states. GodUI animates each one differently, so a tooltip grows on first hover and stays calm when you hop across a toolbar.
↓Scroll to step through it
Delayed vs instant
A Radix tooltip has more states than open and closed. Its data-state is
delayed-open when it opened after the hover delay, instant-open when it
skipped the delay, or closed.
It skips the delay in two cases: keyboard focus, and moving from one trigger
to another within skipDelayDuration (300ms by default). In both, the user is
already reading tooltips, so a full grow-and-drift on each one is noise.
GodUI gives delayed-open the full godui-popover-in and instant-open a
plain godui-fade-in.
Side-aware drift
The delayed entrance is the same keyframe Popover uses. The scale grows from Radix's transform origin, the edge nearest the trigger, and the per-side variables make the tooltip drift 4px out of it. A tooltip above its trigger starts 4px low and rises into place.
The arrow lives inside the content, so it moves with it.
The result
Hover the first icon and wait for its tooltip to grow in. Then slide across the toolbar: each next tooltip fades in place while the previous one leaves.
- opacity
- 0 → 1
- scale
- 0.96 → 1
- translate
- 0 4px → 0 0
"… data-[state=delayed-open]:animate-godui-popover-in data-[state=instant-open]:animate-godui-fade-in data-[state=closed]:animate-godui-popover-out"Anatomy of the GodUI Tooltip
Radix has three open states. GodUI animates each one differently, so a tooltip grows on first hover and stays calm when you hop across a toolbar.
- opacity
- 0 → 1
- scale
- 0.96 → 1
- translate
- 0 4px → 0 0
"… data-[state=delayed-open]:animate-godui-popover-in data-[state=instant-open]:animate-godui-fade-in data-[state=closed]:animate-godui-popover-out"Delayed vs instant
A Radix tooltip has more states than open and closed. Its data-state is
delayed-open when it opened after the hover delay, instant-open when it
skipped the delay, or closed.
It skips the delay in two cases: keyboard focus, and moving from one trigger
to another within skipDelayDuration (300ms by default). In both, the user is
already reading tooltips, so a full grow-and-drift on each one is noise.
GodUI gives delayed-open the full godui-popover-in and instant-open a
plain godui-fade-in.
ease-spring-snappy
"origin-(--radix-tooltip-content-transform-origin)data-[side=bottom]:[--godui-enter-y:-0.25rem]data-[side=top]:[--godui-enter-y:0.25rem]data-[side=left]:[--godui-enter-x:0.25rem]data-[side=right]:[--godui-enter-x:-0.25rem]"Side-aware drift
The delayed entrance is the same keyframe Popover uses. The scale grows from Radix's transform origin, the edge nearest the trigger, and the per-side variables make the tooltip drift 4px out of it. A tooltip above its trigger starts 4px low and rises into place.
The arrow lives inside the content, so it moves with it.
The result
Hover the first icon and wait for its tooltip to grow in. Then slide across the toolbar: each next tooltip fades in place while the previous one leaves.
What's animated
| Interaction | Keyframe / mechanism | Properties | Easing | Duration |
|---|---|---|---|---|
Open after the delay (delayed-open) | godui-popover-in | opacity, scale 0.96 → 1 from the trigger, translate 4px → 0 | ease-spring-snappy | 260ms |
Focus, or hop within skip delay (instant-open) | godui-fade-in | opacity | ease-spring-snappy | 260ms |
| Close | godui-popover-out | the same as open, reversed | ease-out-expo | 150ms |
Why GPU-only
Every state animates opacity, plus scale and translate for delayed opens.
All of them are composited.
Reduced motion
--godui-motion: 0 removes the scale and drift, so every state only fades,
in 150ms.
Replacing shadcn
npx shadcn add @godui/tooltip overwrites components/ui/tooltip.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.