Creative#UI design#Motion graphics
hairline: 33 isometric line figures that answer the pointer
hairline turns isometric line art into interactive React or DOM components — cards rise, pillars heave, turntables coast — zero deps, one shadcn command.
Project facts
GitHub Ecosystem- License
- MIT
- Language
- TypeScript
- Stars
- 1,924
- Data checked
- 2026-10-14
Snapshot figures reflect the check date and may change over time.
Decorative visuals on product pages live at two extremes: a static illustration that never moves, or the same component-library motion everyone ships. hairline (TypeScript, MIT, 1,924 stars as of 2026-10-14) takes a third path — thirty-three isometric line figures, drawn in one consistent visual language of hairline strokes and generous whitespace, and every one of them answers the pointer.

Core features
- Every figure has physics: in the riffle card tray the card under the pointer stands up and arrow keys walk the deck; terrain’s eighty-one pillars rise around the cursor; terminal scrolls its history back and lifts the line under the pointer with its neighbours; flick the turntable and it coasts to the nearest quarter turn; in branches the commit under the pointer rises with its own history behind it.
- React is optional: the core is dependency-free ESM —
terrain(document.getElementById(...))is the whole integration, and the Svelte action shapeuse:terrain={{ intensity }}works as is. The React 18+ wrapper is an optional peer, and a Server Component can render it without adding “use client”. - One shadcn command:
npx shadcn@latest add https://hairline.lucasmarkes.com/r/hairline.jsoninstalls the package plus a wrapper that reads your theme’s tokens. - Tunable intensity: every figure takes an intensity option — the ripple spreads further, more pillars rise, pointer trails linger longer; the live site has a slider so you can judge before committing.
- Simple sizing rules: a figure draws into the element you give it, fills its width at a fixed 5:4 aspect ratio, and
update()changes options whiledestroy()removes everything it added.
Typical use cases
- A front-end engineer who wants a hero section people actually touch: drop in a
<Terrain />or<Turntable />and the first hover already responds. - Docs and product sites that need one illustration style everywhere: the same line language covers cards, terminals, laptops, phones and commit graphs — no more stitching three illustration packs.
- Teams not on React: the Svelte action and the vanilla DOM API work the same, so a framework migration doesn’t rewrite the visuals.
Quick start
npm i @lucasmarkes/hairline
import { Terrain } from '@lucasmarkes/hairline/react';
export function Hero() {
return <Terrain />;
}
The component fills its parent’s width at 5:4. Try the intensity slider at hairline.lucasmarkes.com first and pick your setting before choosing a slot.
Summary
For front-end work that should be remembered — heroes, empty states, product explainers. Not for charts that carry information: the motion is decorative, and slots that can’t give up a 5:4 box will fight it. TypeScript, MIT, 1,924 stars as of 2026-10-14, last pushed 2026-10-08; a personally maintained repo, so long-term cadence depends on the author.
One thing to know first: every figure fills its parent’s width at a fixed 5:4 aspect ratio, so layouts need to reserve that box, and intensity is essentially the whole tuning surface — there is no playback control beyond play. For the same visual language elsewhere, the animate skill brings isometric line art to agent-generated stills while hairline brings it to the live DOM; diagram-design-agent-skill covers another way to give agent-drawn diagrams taste.