Anatomy of the GodUI Combobox
A Base UI popup animated with the same keyframe as every Radix popover, and chips that make room for each other with FLIP.
↓Scroll to step through it
Base UI, not Radix
shadcn/ui builds its combobox on Base UI, so GodUI does too: replacing the file mustn't change which library your call sites talk to.
Base UI names things differently from Radix. The popup gets data-open and
data-closed instead of data-state, and publishes its transform origin as
--transform-origin. The motion is the same godui-popover-in keyframe
every Radix popover uses, though. Base UI waits for running CSS animations
before it unmounts the popup, so the exit keyframe plays out.
Chips that make room
Removing a chip changes the flex-wrap layout: every chip after it moves left,
and some may jump up a line. GodUI lets that layout happen in one step, then
plays each moved chip's old position back to its new one with translate,
using the same useFlipGroup hook as Accordion.
Each chip renders a tiny signal that bumps a counter on mount and unmount,
the exact commit where the layout changed. Chips added after the container
mounted pop in with godui-fade-scale-in; the chips of the initial value are
part of the first paint and don't. If you remove chips quickly, a chip that's still
sliding starts its next slide from where it's drawn.
The result
Remove a chip with its ×, then type in the input and add one back.
- opacity
- 0 → 1
- scale
- 0.96 → 1
- translate
- 0 -4px → 0 0
<ComboboxPrimitive.Popupdata-slot="combobox-content"className={cn( "… origin-(--transform-origin) … data-[side=bottom]:[--godui-enter-y:-0.25rem] … data-open:animate-godui-popover-in data-closed:animate-godui-popover-out", className,)}/>Anatomy of the GodUI Combobox
A Base UI popup animated with the same keyframe as every Radix popover, and chips that make room for each other with FLIP.
- opacity
- 0 → 1
- scale
- 0.96 → 1
- translate
- 0 -4px → 0 0
<ComboboxPrimitive.Popupdata-slot="combobox-content"className={cn( "… origin-(--transform-origin) … data-[side=bottom]:[--godui-enter-y:-0.25rem] … data-open:animate-godui-popover-in data-closed:animate-godui-popover-out", className,)}/>Base UI, not Radix
shadcn/ui builds its combobox on Base UI, so GodUI does too: replacing the file mustn't change which library your call sites talk to.
Base UI names things differently from Radix. The popup gets data-open and
data-closed instead of data-state, and publishes its transform origin as
--transform-origin. The motion is the same godui-popover-in keyframe
every Radix popover uses, though. Base UI waits for running CSS animations
before it unmounts the popup, so the exit keyframe plays out.
function ComboboxChips({ className, ref, ...props }) {const chipsRef = React.useRef<HTMLDivElement | null>(null);const [version, bump] = React.useReducer((n: number) => n + 1, 0);useFlipGroup(chipsRef, version, { selector: '[data-slot="combobox-chip"], [data-slot="combobox-chip-input"]',});// … <ComboboxChip> bumps on mount and unmount}Chips that make room
Removing a chip changes the flex-wrap layout: every chip after it moves left,
and some may jump up a line. GodUI lets that layout happen in one step, then
plays each moved chip's old position back to its new one with translate,
using the same useFlipGroup hook as Accordion.
Each chip renders a tiny signal that bumps a counter on mount and unmount,
the exact commit where the layout changed. Chips added after the container
mounted pop in with godui-fade-scale-in; the chips of the initial value are
part of the first paint and don't. If you remove chips quickly, a chip that's still
sliding starts its next slide from where it's drawn.
The result
Remove a chip with its ×, then type in the input and add one back.
What's animated
The check pops in on the item you select, and only then: checks and chips already there when the combobox or popup mounts stay still.
| Interaction | Keyframe / mechanism | Properties | Easing | Duration |
|---|---|---|---|---|
| Open | godui-popover-in | opacity, scale 0.96 → 1 from the anchor, translate 4px → 0 | ease-spring-snappy | 260ms |
| Close | godui-popover-out | the same, reversed | ease-out-expo | 150ms |
| Selected check | godui-fade-scale-in from 50% | scale, opacity | ease-spring-snappy | 260ms |
| Chip added | godui-fade-scale-in | scale, opacity | ease-spring-snappy | 260ms |
| Chip removed | the chips after it FLIP | translate | ease-out-expo | 260ms |
| Item highlight | snaps | none | none | none |
Why GPU-only
The popup animates opacity, scale and translate; chips animate scale,
opacity and translate. shadcn's chips container transitions color and
box-shadow; GodUI's doesn't.
Reduced motion
--godui-motion: 0 turns the popup, check and chip entrances into fades, and
useFlipGroup moves chips into place without sliding.
Replacing shadcn
npx shadcn add @godui/combobox overwrites components/ui/combobox.tsx and
installs the GodUI button and input-group it uses. Exports, props and
data-slot attributes match shadcn/ui new-york-v4, so existing imports keep
working.