KQ-1358 · STACK/EXTRUDE
Stack Extrude
Every word is a real 3D block — a stack of copies at true translateZ depths under an em-based perspective, the sides in a pigment that darkens toward the back — and on arrival the words peel up off the page one after another, each word's height on springs.recoil and its stack spreading on springs.glide while a soft contact shadow widens and pales. The blocks turn to face the pointer on springs.glide and drift home on springs.drift; a press pushes them into the page on springs.flick, nearest first, and they pop back on recoil in a ripple. Arrow keys turn them, holding Space or Enter presses them, and Home or Escape squares them up.
Basinworks · cast stone
Poured Stone
Arrow keys turn the words; hold Space to press them into the page.flat · printed on the page
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/stack-extrude
Props
- text
- string
- default —
- The words, each set as its own extruded block.
- as
- "h1" | "h2" | "h3" | "p" | "span" | "div"
- default "p"
- The element rendered.
- layers
- number
- default 12
- Copies in each word's stack, 2 to 32: few read as stepped slabs, many as a solid.
- depth
- number
- default 0.3
- How deep the extrusion runs, em.
- tilt
- number
- default 12
- How far the pointer turns the blocks, degrees.
- angle
- [number, number]
- default [12, -18]
- The resting angle [rotateX, rotateY], degrees; held still under reduced motion.
- face
- string
- default "var(--foreground)"
- Face colour, any CSS colour.
- side
- string
- default "oklch(from var(--accent) 0.56 c h)"
- The sides' pigment, any CSS colour.
- shade
- number
- default 0.6
- How much the sides darken toward the back, 0 to 1.
- shadow
- number
- default 0.5
- Strength of the soft shadow on the page, 0 to 1.
- lift
- number
- default 0.14
- The gap between block and page at rest, em.
- press
- number
- default 0.7
- How far a press pushes the blocks into the page, 0 to 1.
- trigger
- "mount" | "inView" | "hover" | "manual"
- default "inView"
- What lifts the words off the page.
- once
- boolean
- default true
- Lift once; false lays the words flat again when it leaves view or the hover ends.
- play
- boolean
- default —
- Manual trigger: true lifts, false lays flat.
- delay
- number
- default 0
- Wait before the first word lifts, ms.
- stagger
- number
- default 90
- Gap between words lifting, ms.
- duration
- number
- default 700
- Time scale of the lift springs, ms; their damping ratios are kept.
- interactive
- boolean
- default true
- Pointer tilt, press, and keyboard turning.
- onPressChange
- (pressed: boolean) => void
- default —
- A press began (true) or ended (false).
- onComplete
- () => void
- default —
- The lift has settled.
- className
- string
- default —
- Size and type; perspective and depth are in em, so the look holds from caption to hero.
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | — | The words, each set as its own extruded block. |
| as | "h1" | "h2" | "h3" | "p" | "span" | "div" | "p" | The element rendered. |
| layers | number | 12 | Copies in each word's stack, 2 to 32: few read as stepped slabs, many as a solid. |
| depth | number | 0.3 | How deep the extrusion runs, em. |
| tilt | number | 12 | How far the pointer turns the blocks, degrees. |
| angle | [number, number] | [12, -18] | The resting angle [rotateX, rotateY], degrees; held still under reduced motion. |
| face | string | "var(--foreground)" | Face colour, any CSS colour. |
| side | string | "oklch(from var(--accent) 0.56 c h)" | The sides' pigment, any CSS colour. |
| shade | number | 0.6 | How much the sides darken toward the back, 0 to 1. |
| shadow | number | 0.5 | Strength of the soft shadow on the page, 0 to 1. |
| lift | number | 0.14 | The gap between block and page at rest, em. |
| press | number | 0.7 | How far a press pushes the blocks into the page, 0 to 1. |
| trigger | "mount" | "inView" | "hover" | "manual" | "inView" | What lifts the words off the page. |
| once | boolean | true | Lift once; false lays the words flat again when it leaves view or the hover ends. |
| play | boolean | — | Manual trigger: true lifts, false lays flat. |
| delay | number | 0 | Wait before the first word lifts, ms. |
| stagger | number | 90 | Gap between words lifting, ms. |
| duration | number | 700 | Time scale of the lift springs, ms; their damping ratios are kept. |
| interactive | boolean | true | Pointer tilt, press, and keyboard turning. |
| onPressChange | (pressed: boolean) => void | — | A press began (true) or ended (false). |
| onComplete | () => void | — | The lift has settled. |
| className | string | — | Size and type; perspective and depth are in em, so the look holds from caption to hero. |