Composable parts,
fully yours.
Accessible, dependency-light React components. Copy the source or install with the shadcn CLI — then edit freely.
Move across the text.
The dots keep reacting under solid content. Swipe fast and they get thrown further than the same path taken slowly. Click for a shockwave.
Critical damping is 2·√stiffness — about 19 here. Below it the dots overshoot and ring; above it they crawl home. Drop drag to 0 and the field stops caring how fast you move, which is how a lerped grid always behaves.
Magnetic Dots
A dot grid backdrop driven by a real spring, not a lerp. Dots carry velocity, so they get flung past their rest position and ring back — and the field reads how fast you move, not just where you are: a swipe throws dots further than the same path taken slowly. Click for an expanding shockwave. The canvas is pointer-transparent and listeners bind to the container, so it keeps reacting under headlines and buttons. Fixed 1/120s sub-stepping keeps the spring stable and frame-rate independent; the dirty rect bounds the cost to the influence radius.
Move across the field — or drag, on touch. React never re-renders while it moves.
Damping under ~10 overshoots and wobbles; over ~30 it goes syrupy. The default is 18 — the difference between those numbers is the entire feel of the component.
Iso Momentum
An isometric slab field where every slab's height tracks pointer proximity and springs independently. The pointer flows through a motion value into each slab's path `d` string at 60fps — no React state, no re-renders while it moves. Pointer events, so it works under touch and pen, not just a mouse.
Scroll me — fast, slow, and backwards
01 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
02 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
03 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
04 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
05 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
06 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
07 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
08 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
09 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
10 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
11 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
12 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
13 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
14 Flick down and the strip accelerates left and shears with it. Flick up hard enough and it crosses zero and runs the other way. Let go and it coasts back to the base drift instead of stopping dead — that easing is the whole point.
Drop decay to 0.05s and the strip snaps to a halt the instant you stop — that is exactly the broken-feeling version. 0.35s is the default.
Velocity Marquee
An infinite strip whose speed and skew couple to scroll velocity — it accelerates as you scroll, reverses when you scroll back, and eases to a base drift when you stop. The wrap is a modulo on a measured content width (seamless in both directions, no CSS-animation restart), and the velocity is smoothed by an exponential moving average so it coasts instead of snapping.
Type that answers the cursor.
Every glyph measures its own distance from the pointer, springs independently, and resolves to a font-variation-settings string. The boxes are pinned to their resting widths, so nothing reflows and no cached centre ever goes stale — the letters swell in place instead of shoving each other along the line.
Move across the text — zero React re-renders
Push peak weight to 900 and drag across a word: the line holds its wrapping exactly. Without the pinned boxes, that is the setting where it starts to jitter and re-wrap under the cursor.
Kinetic Type
Per-glyph variable-font response to pointer proximity — axes swell near the cursor and relax away from it, continuously and reversibly. Glyph boxes are pinned to their resting widths so animating `wght` can't shift neighbours, stale the cached centres, or re-wrap the line; centres are measured once and only re-read on resize or font swap. The accessibility tree reads one clean string.
Scrub slowly through the middle — no crossings, no knots.
Click repeatedly — it reverses cleanly from wherever it is.
3 subpaths → 2 · the middle line collapses into the X
Morph Icon
True SVG path interpolation between two icons — not a cross-fade. Ships its own path normalizer with no dependency: a cursor-based parser (correct on packed arc flags, where a number regex silently fails), every command reduced to cubics including the full endpoint→centre arc conversion, equal-arc-length resampling, and a rotation/direction search that minimises total travel so the morph never ties itself in a knot. Multiple subpaths are paired by centroid, and an unmatched one collapses into its nearest counterpart.
Driven by --st-progress, cover · ease-in-out
Driven by --st-progress, cover · ease-in-out
Driven by --st-progress, cover · ease-in-out
Driven by --st-progress, cover · ease-in-out
Driven by --st-progress, cover · ease-in-out
Driven by --st-progress, cover · ease-in-out
Scroll each panel. The two columns must stay locked together — the left one uses animation-timeline: view() where the browser has it, the right one is forced onto the rAF fallback. Both drive the same custom property, so there is only one thing rendering.
Scroll Timeline
Continuous scroll-linked animation on the native Scroll-Driven Animations API, with a JS fallback that is numerically identical rather than merely similar. Both paths drive one registered custom property, `--st-progress`, so there is a single renderer and only the source of the number changes — the spec's four view-progress ranges and a real cubic-bézier solver make the fallback match to ~1e-6. Set `forceFallback` on one of two instances to check it yourself.
Drop spacing to 8 or below to push past 5,000 dots and watch the fps hold — the per-frame cost is bounded by the influence radius, not the grid size. Scroll the field out of view and the rAF loop is cancelled outright, not left spinning.
Dot Field
A canvas dot grid that reacts around the cursor, at a density SVG can't reach. The per-frame cost is bounded by the influence radius rather than the dot count: only the dirty rect is cleared and redrawn, and dots are batched into 16 paths by quantised alpha instead of one fill each. DPR-correct on retina, and the rAF loop is genuinely cancelled — offscreen, backgrounded, or simply settled.
Drag, or focus a row and press space
- Pointer capturedrag survives leaving the row
- Cached geometrysnapshotted once at lift — the whole reason this doesn't oscillate
- Auto-scrollnear the container edges
- Keyboard modespace, arrows, space
- Live announcementsaria-live on every move
- Drop animationglides into the final slot
- Ragged heightslayout is exact, not a uniform pitch
- Zero re-renderstransforms written straight to the DOM
Try to make it oscillate: park the cursor exactly on a boundary and jiggle. The target index is a pure function of the frozen snapshot and the pointer delta, so there is no feedback loop to buzz in.
Tab to a row, then Space to lift, ↑↓ to move, Space to drop, Esc to cancel — the entire flow without a mouse.
a → b → c → d → e → f → g → h
Sortable List
Drag-to-reorder, hand-written, with a keyboard mode that shares the same code path. The target index is a pure function of geometry frozen at lift and the pointer delta — monotonic in the delta by construction, so there is no feedback loop to oscillate in at any drag speed. Pointer capture, edge auto-scroll, a drop animation into the final slot, and `aria-live` announcements throughout.
Three detents
Flick the handle hard and it carries past a detent. Drag it slowly past the same point and it settles on the nearer one — the position is identical, only the velocity differs.
- Arbitration: scroll the list down, then drag down from inside it — the sheet does not move. Scroll back to the top and the same gesture drags the sheet.
- Locked for the gesture: start a downward drag mid-list and keep going past the top. The sheet still doesn't grab it — the decision was made on the first move.
- Rubber band: pull the handle up past the tallest detent.
Drag Sheet
A bottom sheet with snap points that feels native rather than like a `<div>` with a transition — three formulas and a state machine. Snapping is velocity-projected, so a flick and a slow drag past the same point resolve differently; bounds use Apple's rubber-band curve; and the drag-vs-scroll decision is made on the first `pointermove` past a 4px threshold and locked for the whole gesture, which is what stops a sheet from stealing a scroll halfway through.
Text that develops into focus.
Each line de-blurs and fades up as it enters the viewport — fires once, and respects reduced-motion.
Text Reveal
Reveals its children with a soft de-blur + fade as they scroll into view — the 'text develops into focus' effect. Fires once, respects reduced-motion, and is polymorphic via `as` so it can wrap a heading, paragraph, or a single list item.
MacBook Mockup
A MacBook whose lid tilts open as it scrolls into view, revealing whatever you pass as `children` on the screen. The screen UI is authored at a fixed 900px design width and uniformly scaled to fit — so it never reflows across breakpoints. Reduced-motion users get the lid already open.
AI SEO Console
Ask anything. I'll audit, plan, and execute across your site.
- Keywords14 high-intent gaps vs. top 3 competitorsGenerate content briefs
- Content8 pages with thin content (<400 words)Expand with AI outlines
- Tech SEOLCP 3.8s on /blog/seo-tipsInline critical CSS
- On-PageDuplicate H1s on 14 pagesRewrite headings
- Backlinks5 broken inbound links losing equityReclaim via redirects
- SchemaMissing FAQ schema on 12 pagesAdd structured data
- Organic Search18.2K / 24K
- AI Overviews4.1K / 6K
- Long-tail Queries2.8K / 5K
- Branded Search9.4K / 10K
- /ai-content-briefpos 3.22.1K
- /seo-audit-toolpos 5.81.6K
- /keyword-gappos 7.1940
- /technical-seopos 9.4612
Product Mock
A 'your product, running' analytics dashboard mock — an agentic SEO console with a chat launcher, a recommendations queue, growth pacing, and a live status bar. Authored at a fixed 960×540 design box and uniformly scaled to fit, so the dense UI stays crisp and never reflows. Themeable accent, ships with no image assets (the mark is inline SVG).
Iso Search
Isometric illustration — a magnifying glass floating over a grid of opportunity dots, with a radar sweep inside the lens. Themeable accent via `brand`; animation stills under reduced-motion.
Iso Priority
Isometric illustration — a stack of iso cards with the top (priority) card lifting off and pinned, signalling a selection. Themeable accent via `brand`; reduced-motion aware.
Iso Deploy
Isometric illustration — an iso cube 'system' with a pulsing status light, shipping small chips out along the iso-up axis. Themeable accent via `brand`; reduced-motion aware.
Iso Growth
Isometric illustration — ascending iso bars with a rising curve threading through their tops, looping to suggest compounding growth. Themeable accent via `brand`; reduced-motion aware.
The user wants a smooth reveal. Measure the content height implicitly with a grid row, then ease grid-template-rows from 0fr to 1fr so nothing reflows. Shimmer the label while streaming, and collapse automatically once the answer is ready.
Reasoning
The collapsible "thinking" trace from AI chat UIs — shimmers while streaming, then auto-collapses to a summary. Height animates with the grid-rows technique (no JS measurement), and it stays fully accessible and dependency-light.
Tool Call Timeline
The collapsible agent trace from AI product UIs — nested tool calls with running / done / failed states, a live duration counter, and per-call results. Recursive tree layout with a connector spine; expand/collapse animates height via the grid-rows technique (no reflow flicker).
To reveal a panel smoothly, ease grid-template-rows from 0fr to 1fr1 so it animates to its real height with no measurement. Gate the transition behind a reduced-motion check2 for accessibility, and render any floating card through a portal3 so it never gets clipped by an overflow container.
- [1]Animating height with grid-template-rowscss-tricks.com
Transitioning grid-template-rows from 0fr to 1fr lets a container ease open to its content's natural height — no fixed pixel value, no JS measurement.
- [2]prefers-reduced-motion — MDNdeveloper.mozilla.org
A media feature that detects whether the user has asked the system to minimize non-essential motion, so animations can be toned down or removed.
- [3]Rendering overlays with createPortalreact.dev
createPortal renders children into a different part of the DOM — ideal for popovers and tooltips that must escape an ancestor's overflow clipping.
Citation Cards
Inline source markers with hover cards, the way AI answers footnote their claims. The card is portaled and collision-aware — it flips above the marker near the viewport edge and shifts to stay on-screen with its arrow still pointing home. Hovering a marker or its source row syncs both ways, and it's fully keyboard-navigable.
- Your deployment is liveVercel
- 3 issues assigned to youLinear
- Re: flaky test in CIGitHub
- Dana left 2 commentsFigma
Archiving fails ~40% — watch it roll back.
Optimistic Queue
A list whose archive action applies optimistically — the row collapses the instant you click, before the request resolves. Success drops it; failure rolls it back, expanding it right into place. The trick: rows are never unmounted on the optimistic step, only after the request confirms, so the exit animation can reverse mid-flight with no re-mount gymnastics. A live queue tracks in-flight actions. Keyboard-accessible and monochrome.