KQ-2088 · GOAL/BLOOM

Goal Bloom

A radial plan with the goal in a disc at the centre, sub-goals as pills on the first ring and their steps on the second, each sub-goal owning a sector and a petal. As the agent plans, every new node blooms out of its parent along its own angle on springs.glide while its siblings re-space round the ring on the same spring, each node's angle and radius animated in polar coordinates so a re-spacing sweeps round the circle; when a branch finishes, its steps fold back into the sub-goal, the sub-goal moves in toward the centre and its petal fills from the centre on springs.recoil, so progress reads as the flower closing while the goal's ring fills on glide. It is a tree: Left and Right move round a ring, Enter steps out a ring, Escape steps back in, and under 32rem the bloom becomes an indented list with the same states and keys.

KQ-2088 · GOAL/BLOOM

Coldbrook · Larch Street

Store opening plan

Planning…

Left and Right move round a ring, Enter steps out to the next ring, Escape steps back in. In the list, Up and Down move, Right steps out, Left steps in.

Open Coldbrook on Larch Street5/11
Fit-out1/3
Install the espresso barDone
Wire the back officeIn progress
Paint and hang the signTo do
Hiring and training1/3
Hire a shift leadDone
Hire two baristasIn progress
Run training weekTo do
Stock and suppliers0/2
Sign the roaster contractBlocked
Order opening stockTo do
Launch weekPlanning

planning · 11 steps · 1 of 5 branches done

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/goal-bloom

Props

petalShape
"leaf" | "round" | "wedge"
default "leaf"
Every petal, outline and fill: a pointed leaf, a blunt round petal, or an annular wedge.
ringGap
number
default 64
Units between rings, 40 to 76, in a bloom 400 units across — how open the flower is. Deeper rings scale to fit.
accent
string
default "var(--accent-bright)"
Petals, active nodes, the goal's ring and Approve — any CSS colour. Filled petals take its hue at a fixed lightness so they read alike in both themes.
goal
GoalBloomGoal
default defaultGoalBloomGoal
The goal at the centre: title, a short form for the disc, and a note for the panel.
nodes
GoalBloomNode[]
default defaultGoalBloomNodes
Sub-goals and steps as one flat list linked by parent (null for a sub-goal of the goal): id, title, short, status (todo, active, done, blocked, planning), owner, note. A node with children takes its status from them.
depth
number
default 2
How many rings to draw, 1 to 3; deeper nodes are left out.
status
"ready" | "planning" | "blocked" | "loading"
default "ready"
The plan as a whole: planning holds Approve and draws new nodes dashed, blocked flags the plan, loading draws the bud alone. An empty nodes list shows the empty state.
selected / defaultSelected
string | null
default — / null
The selected node id, controlled or initial; null is the goal.
onSelectNode
(id: string | null) => void
default —
Fires from the click or key that moved the selection.
onAddSubgoal
(parentId: string | null) => void
default —
Add a step (or Add a sub-goal, on the goal) was pressed in the detail panel.
onApprove
() => void
default —
Approve plan was pressed.
intro
boolean
default true
Bloom out of the centre the first time the plan is seen.
title
string
default "Store opening plan"
The plan's name.
product
string
default "Coldbrook · Larch Street"
Who it is for, shown above the title.
label
string
default the title
The tree's accessible name.
disabled
boolean
default false
Read only: nothing can be added or approved.
className
string
default —
Classes for the outer surface.