KQ-1488 · BRANCH/OUT
Branch Out
A menu that grows its options like branches: opening grows a short stem out of the trigger, then a branch per option draws along a curve whose bend you set, nearest the centre line first and staggered, and as each arrives a bud swells at its end and the option opens out of it on springs.snap. Hovering or focusing an option thickens and tints its branch and grows a leaf at its middle, and closing retracts everything in reverse on the exit ease. It is a real menu button: arrows move a roving focus, Home and End jump, letters type ahead, Enter or Space chooses and Escape closes and returns focus to the trigger.
menu closed · press add a block
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/branch-out
Props
- items
- { id: string; label: string; icon?: ReactNode; hint?: string; disabled?: boolean }[]
- default —
- The options, top to bottom; disabled ones are aria-disabled and skipped by the arrow keys.
- onSelect
- (id: string) => void
- default —
- Called with the chosen option's id.
- open / defaultOpen
- boolean
- default — / false
- Whether the menu is open, controlled or uncontrolled.
- onOpenChange
- (open: boolean) => void
- default —
- Fires from the press, key or outside click that opened or closed it.
- label
- string
- default "Add"
- The trigger's accessible name and the menu's; also shown beside the trigger while closed.
- icon
- ReactNode
- default a plus
- The trigger's icon; it turns 45° on springs.snap while open.
- side
- "right" | "left"
- default "right"
- Which way the branches grow from the trigger.
- spread
- number
- default 0.5
- How wide the fan opens, 0 to 1: the options' spacing and the radius of the arc they sit on.
- curve
- number
- default 0.6
- How the branches bend, 0 to 1: straight twigs to S-shaped tendrils.
- stagger
- number
- default 60
- Time between one branch and the next as they grow, in ms; capped so the whole growth stays near 600ms.
- color
- string
- default "var(--accent-bright)"
- The trigger's fill and the colour a branch, bud and leaf take when their option is hot; any CSS colour.
- branchColor
- string
- default "var(--hairline-strong)"
- The branches and buds at rest; any CSS colour.
- closeOnSelect
- boolean
- default true
- Close the menu (and return focus to the trigger) after an option is chosen.
- showLabel
- boolean
- default true
- Show `label` beside the trigger while closed; it gives way as the branches grow.
- size
- "sm" | "md" | "lg"
- default "md"
- Max width (280, 320 or 360px), trigger and option height.
- sound
- boolean
- default false
- Play a pop as each option buds (rising in pitch), a tick as focus moves and a lower tick on choose and close; only in answer to a press or key.
- disabled
- boolean
- default false
- Disables the trigger.
- className
- string
- default —
- Classes for the root box, whose height follows the item count and spread.
| Prop | Type | Default | Description |
|---|---|---|---|
| items | { id: string; label: string; icon?: ReactNode; hint?: string; disabled?: boolean }[] | — | The options, top to bottom; disabled ones are aria-disabled and skipped by the arrow keys. |
| onSelect | (id: string) => void | — | Called with the chosen option's id. |
| open / defaultOpen | boolean | — / false | Whether the menu is open, controlled or uncontrolled. |
| onOpenChange | (open: boolean) => void | — | Fires from the press, key or outside click that opened or closed it. |
| label | string | "Add" | The trigger's accessible name and the menu's; also shown beside the trigger while closed. |
| icon | ReactNode | a plus | The trigger's icon; it turns 45° on springs.snap while open. |
| side | "right" | "left" | "right" | Which way the branches grow from the trigger. |
| spread | number | 0.5 | How wide the fan opens, 0 to 1: the options' spacing and the radius of the arc they sit on. |
| curve | number | 0.6 | How the branches bend, 0 to 1: straight twigs to S-shaped tendrils. |
| stagger | number | 60 | Time between one branch and the next as they grow, in ms; capped so the whole growth stays near 600ms. |
| color | string | "var(--accent-bright)" | The trigger's fill and the colour a branch, bud and leaf take when their option is hot; any CSS colour. |
| branchColor | string | "var(--hairline-strong)" | The branches and buds at rest; any CSS colour. |
| closeOnSelect | boolean | true | Close the menu (and return focus to the trigger) after an option is chosen. |
| showLabel | boolean | true | Show `label` beside the trigger while closed; it gives way as the branches grow. |
| size | "sm" | "md" | "lg" | "md" | Max width (280, 320 or 360px), trigger and option height. |
| sound | boolean | false | Play a pop as each option buds (rising in pitch), a tick as focus moves and a lower tick on choose and close; only in answer to a press or key. |
| disabled | boolean | false | Disables the trigger. |
| className | string | — | Classes for the root box, whose height follows the item count and spread. |