KQ-1399 · BLOCK/DISSOLVE
Block Dissolve
A two-state transition for any two children: the first dissolves into the second block by block, each clipped by a path rebuilt every frame so even transparent content never shows through. The blocks come from a seeded quadtree and flip in a random, diagonal or pointer-led order with coarse blocks first and finer halves and quarters filling the holes, so the block size eases from coarse to fine as the dissolve finishes, and a faint glint rides the blocks that have just flipped; progress is one tween on the move ease and turns back over the same blocks if the pointer leaves early. With hover or press it is a single toggle button: Enter and Space swap it, touch taps swap it, and only the state on show is exposed to assistive technology.
Summit cam · 06:40
4° · clear
dawn · pointer order · 24 px blocks · 800 ms
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/block-dissolve
Props
- children
- ReactNode
- default —
- Exactly two children: the first state, then the second. Any content — images, cards, charts.
- value / defaultValue
- "first" | "second"
- default "first"
- Which state shows.
- onValueChange
- (value: "first" | "second") => void
- default —
- The state changed, from the hover, press or key that changed it.
- trigger
- "hover" | "press" | "manual"
- default "hover"
- What swaps the states: hovering (keys and taps toggle), pressing, or only value.
- block
- number
- default 24
- Coarse block size, px; the run refines to halves and quarters of it.
- order
- "random" | "diagonal" | "pointer"
- default "pointer"
- The order the blocks flip in: seeded scatter, a diagonal sweep, or outward from the pointer.
- duration
- number
- default 800
- How long the whole dissolve takes, ms.
- levels
- 1 | 2 | 3
- default 3
- How many sizes the blocks refine through.
- glint
- number
- default 0.3
- Brightness of the glint on freshly flipped blocks, 0 to 1.
- glintColor
- string
- default "var(--accent-bright)"
- Colour of the glint.
- seed
- number
- default 7
- Seed for the block order and the coarse-to-fine split.
- label
- string
- default "Swap view"
- Accessible name of the toggle, e.g. "Show the night view".
- disabled
- boolean
- default false
- No swapping; the toggle says it is unavailable.
- onComplete
- (value: "first" | "second") => void
- default —
- A dissolve finished on this state.
- className
- string
- default —
- Classes for the box — give it a size or an aspect ratio and a radius.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Exactly two children: the first state, then the second. Any content — images, cards, charts. |
| value / defaultValue | "first" | "second" | "first" | Which state shows. |
| onValueChange | (value: "first" | "second") => void | — | The state changed, from the hover, press or key that changed it. |
| trigger | "hover" | "press" | "manual" | "hover" | What swaps the states: hovering (keys and taps toggle), pressing, or only value. |
| block | number | 24 | Coarse block size, px; the run refines to halves and quarters of it. |
| order | "random" | "diagonal" | "pointer" | "pointer" | The order the blocks flip in: seeded scatter, a diagonal sweep, or outward from the pointer. |
| duration | number | 800 | How long the whole dissolve takes, ms. |
| levels | 1 | 2 | 3 | 3 | How many sizes the blocks refine through. |
| glint | number | 0.3 | Brightness of the glint on freshly flipped blocks, 0 to 1. |
| glintColor | string | "var(--accent-bright)" | Colour of the glint. |
| seed | number | 7 | Seed for the block order and the coarse-to-fine split. |
| label | string | "Swap view" | Accessible name of the toggle, e.g. "Show the night view". |
| disabled | boolean | false | No swapping; the toggle says it is unavailable. |
| onComplete | (value: "first" | "second") => void | — | A dissolve finished on this state. |
| className | string | — | Classes for the box — give it a size or an aspect ratio and a radius. |