Shader Lines Showcase — GLSL Line-Field Component Integration for shadcn (Three.js + React + TypeScript)
A shadcn-style component library documentation page that integrates the Shader Lines GLSL fragment shader — a generative full-screen line field rendered with Three.js inside a single React component (ShaderAnimation). The shader accumulates five offset lines per colour channel, producing warm chromatic streaks at 60 fps. The docs page presents it live, documents the drop-in API, and explains its anatomy for teams adopting it in their own shadcn + Tailwind + TypeScript apps. Generated with Claude Fable 5.
npm install
npm run dev # http://localhost:5173
npm run build # tsc type-check + production build
npm run preview # serve the production buildThis project already supports the three things the brief asks for:
- shadcn project structure —
components.jsondeclares the aliases, and the component lives atsrc/components/ui/shader-lines.tsx. - Tailwind CSS —
tailwind.config.js+postcss.config.js, tokens insrc/index.css. - TypeScript —
tsconfig.jsonwith the@/*→src/*path alias, mirrored invite.config.tsso the demo's import resolves verbatim.
If you are starting from scratch instead, the equivalent setup is:
npm create vite@latest my-app -- --template react-ts
cd my-app
npm i -D tailwindcss postcss autoprefixer && npx tailwindcss init -p
npx shadcn@latest init # writes components.json + the @/ aliasshadcn's convention is that copy-pasteable primitives live in
@/components/ui. The demo imports the component as
@/components/ui/shader-lines. That import only resolves because:
- the
@alias points atsrc(set in bothtsconfig.jsonandvite.config.ts), and - the file physically sits in
src/components/ui/.
If the folder doesn't exist (or the alias points elsewhere), the import fails to
resolve and the build breaks. Keeping the file in components/ui is the
contract every other shadcn component relies on — so create the folder and the
rest is copy-paste.
src/components/ui/shader-lines.tsx — exports ShaderAnimation.
import { ShaderAnimation } from "@/components/ui/shader-lines";
<div className="relative h-[650px] w-full overflow-hidden rounded-xl">
<ShaderAnimation />
<span className="absolute inset-0 grid place-items-center text-7xl text-white">
Shader Lines
</span>
</div>- Props / data — none are required. The component takes two optional
props:
className(classes for the canvas host; defaults to filling its parent) andspeed(clock multiplier, default1). It carries no internal app state and needs no context providers. - State management — self-contained. It keeps Three.js handles in a ref and tears them down on unmount.
- Required assets — no images or icons are needed by the shader itself,
so the brief's "fill image assets with Unsplash" step does not apply. The only
runtime dependency is Three.js, which is vendored locally (see below). The
surrounding docs page uses
lucide-reacticons, per the brief. - Responsive behaviour — the canvas fills whatever box you give it. A
ResizeObserverwatches the parent element (not just the window), so the field re-samples correctly inside flex/grid layouts and on rotation. Give the parent a height (h-[650px],h-screen, etc.) and the rest is automatic. - Best place to use it — as an ambient background behind a hero headline or
CTA, or as a self-contained "live" card. It is
aria-hiddenandpointer-events: none-friendly, so it never steals focus or clicks from the content layered on top.
The behaviour and shader are identical; the integration hardens it for real use:
- Three.js is vendored to
public/vendor/three.min.js(r89) and loaded from there instead of a CDN, so the component runs fully offline. - One shared loader — the
<script>is injected once and reused, so a second instance (or React 18 StrictMode's double-mount) doesn't load Three.js twice. ResizeObservertracks the parent box, not onlywindowresizes.- Clean teardown — animation frame cancelled, renderer disposed, WebGL context released, and the canvas removed on unmount.
className+speedprops for reuse without forking the file.
public/vendor/three.min.js— Three.js r89, downloaded fromhttps://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js.public/fonts/*.woff2— Space Grotesk (display), Inter (body), JetBrains Mono (code/data), latin subsets from Google Fonts, declared with@font-faceinsrc/index.css. No external font requests at runtime.
npm run build—tsc(strict,noUnusedLocals) + Vite production build.- Headless Chromium (Playwright) confirmed: the shader canvas mounts and the browser compositor renders the colourful line field; fonts load locally; the tabbed code panel, copy buttons, and speed dial all work; zero console errors and zero failed network requests (all assets served same-origin).
Part of the Shaders collection in the claude-directory — an open-source gallery of AI-generated UI built with Claude Fable 5. Browse the live gallery.
