KQ-1114 · CORNER/PIP
Corner PiP
A floating player inside its own frame that you throw into a corner: it follows the finger 1:1 and rubber-bands past the edges, and on release its momentum is projected with a deceleration set by friction, the corner whose quadrant the projected centre lands in is where it goes, and a landing spring that takes the release velocity carries it there, overshooting more the more slippery it is. A throw off a side edge tucks it into a tab you pull or press to bring back. The grip is a button whose arrow keys move it corner to corner and into the edge, with the same flight, swish and soft thud.
call · bottom right · fling it to a corner
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/corner-pip
Props
- player
- ReactNode
- default —
- What plays inside the floating player. Its own controls work as usual; a drag that starts on them still moves the player.
- children
- ReactNode
- default —
- The screen behind the player: whatever the frame shows.
- label
- string
- default —
- The player's name: its accessible label, and the names of its grip ("Move …") and tab ("Show …").
- value / defaultValue
- { corner: "top-left" | "top-right" | "bottom-left" | "bottom-right"; tucked: boolean }
- default { corner: "bottom-right", tucked: false }
- Where the player sits: a corner, and whether it is tucked into that corner's side edge. Controlled or uncontrolled; a refused move flies back.
- onValueChange
- (value: CornerPipValue) => void
- default —
- Fires from the throw or the key that moved the player, with its new place.
- friction
- number
- default 0.5
- How quickly a throw dies, 0 to 1. Low friction coasts further and overshoots the corner; high friction needs a hard fling and settles flat.
- inset
- number
- default 12
- The corner margin in px, 0 to 24.
- tuck
- boolean
- default true
- A throw whose projection leaves through a side edge tucks the player into that edge, leaving a 22 px tab.
- size
- "sm" | "md" | "lg"
- default "md"
- The player's width: 112, 144 or 176 px at 16:9, never more than 44% of the frame.
- sound
- boolean
- default false
- A swish on a fast throw (higher and louder the faster) and a soft thud on the frame it lands. Nothing plays, and no audio context exists, unless this is on.
- disabled
- boolean
- default false
- Dims the frame and stops every drag and key.
- className
- string
- default —
- Merged onto the frame's classes; the frame is 2:1 unless an aspect class here says otherwise.
| Prop | Type | Default | Description |
|---|---|---|---|
| player | ReactNode | — | What plays inside the floating player. Its own controls work as usual; a drag that starts on them still moves the player. |
| children | ReactNode | — | The screen behind the player: whatever the frame shows. |
| label | string | — | The player's name: its accessible label, and the names of its grip ("Move …") and tab ("Show …"). |
| value / defaultValue | { corner: "top-left" | "top-right" | "bottom-left" | "bottom-right"; tucked: boolean } | { corner: "bottom-right", tucked: false } | Where the player sits: a corner, and whether it is tucked into that corner's side edge. Controlled or uncontrolled; a refused move flies back. |
| onValueChange | (value: CornerPipValue) => void | — | Fires from the throw or the key that moved the player, with its new place. |
| friction | number | 0.5 | How quickly a throw dies, 0 to 1. Low friction coasts further and overshoots the corner; high friction needs a hard fling and settles flat. |
| inset | number | 12 | The corner margin in px, 0 to 24. |
| tuck | boolean | true | A throw whose projection leaves through a side edge tucks the player into that edge, leaving a 22 px tab. |
| size | "sm" | "md" | "lg" | "md" | The player's width: 112, 144 or 176 px at 16:9, never more than 44% of the frame. |
| sound | boolean | false | A swish on a fast throw (higher and louder the faster) and a soft thud on the frame it lands. Nothing plays, and no audio context exists, unless this is on. |
| disabled | boolean | false | Dims the frame and stops every drag and key. |
| className | string | — | Merged onto the frame's classes; the frame is 2:1 unless an aspect class here says otherwise. |