The build guide
How this site was stitched together.
Everything on the homepage was built by hand in this repository: the shader, the palette, the motion. No template, no 3D library, no stock gradients. This page documents each decision so you can make your own.
01 · Concept
Find the one true metaphor, then commit everything to it
Cowichan Souvenir is an embroidery house. The whole redesign hangs on that single fact: the page behaves like a machine laying one continuous thread. The hero is a simulated thread field. A running stitch sews the sections together down the left margin. Buttons wear dashed stitch borders that pull when you hover. The stats panel is a woven care label. Even the monospace type is earned, because embroidery machines literally count stitches.
The test for every element: does it come from the loom, or is it decoration? If a flourish could appear on any other site, it was cut.
02 · Palette
Steal the palette from something the brand already owns
No colour here was invented. The ground is undyed cotton cloth, and every character of type is indigo-dyed thread on top of it. The header and footer keep the deep indigo of the dye vat as bookends. The accents are Carolina, Heliconia and Cardinal, lifted straight from the physical 2026 garment colour card that ships to retailers. When the palette comes from the product, it cannot look generic.
cloth
oklch(0.965 0.007 92)
the daylight ground: undyed cotton
indigo ink
oklch(0.235 0.035 272)
type: everything is written in dyed thread
ink deep
oklch(0.125 0.026 274)
the bookends: header, footer, artwork plates
thread dim
oklch(0.41 0.03 270)
body copy on cloth, ~7:1 contrast
carolina
oklch(0.47 0.1 240)
links and the accented word: a real garment colour
heliconia
oklch(0.55 0.21 358)
hover states, the needle, text selection
03 · Type
One family the brand already licensed, pushed to its extremes
BL Melody and BL Melody Mono were already sitting in this repository's font folder. The redesign registers the full weight range and builds hierarchy from contrast inside one family: ExtraLight 200 for display headlines up to 96px, Regular for prose, and the Mono cut for spec labels, captions and buttons. No second typeface was added.
Melody ExtraLight 200
Melody Regular 400 for reading
Melody Mono for the machine floor · 40wt · 800 spm
04 · The shader
Sixty lines of GLSL beat a megabyte of 3D library
The hero is a single fullscreen triangle and a fragment shader, written by hand against the raw WebGL API. Three sheets of long, near-parallel strands undulate through low-frequency fractal noise and travel in combed bundles, the way silk lies; a sine sweep makes each filament catch light; the pointer parts the strands like a finger. When nobody moves the mouse, a phantom hand keeps circling the hoop. One uniform picks the dye: the homepage runs the day variant (dyed strands laid on cloth), and the panel below runs the night variant (glowing thread on ink).
It pauses off-screen and on hidden tabs, clamps device pixel ratio at 1.75, and for visitors with reduced motion enabled it renders exactly one still frame.
// the pointer parts the strands, the way a finger // parts thread lying on a hoop; no swirl vec2 toP = p - u_pointer; float d = length(toP); float part = exp(-d * d * 6.0) * u_pull; pw.y += part * 0.09 * (toP.y / (d + 0.02)); // a sheet of near-parallel strands, gently undulated // by low-frequency fractal noise, like combed silk float w = fbm(vec2(pw.x * 0.8, pw.y * 1.4) + t * speed); float y = pw.y + warpAmt * (w - 0.5); // warpAmt stays small float id = floor(y * density); // which strand am I on float lines = abs(fract(y * density) - 0.5) / density; float core = smoothstep(thick, 0.0, lines); // silk sheen sweeps along each strand float sheen = 0.55 + 0.45 * sin(pw.x * 4.0 + w * 5.0 + t); // strands travel in bundles, with gaps between skeins float vis = smoothstep(0.18, 0.6, noise(vec2(id * 0.19, seed)));
live · the night dye · move your pointer
05 · Motion
One easing curve, three materials, no confetti
Every animation on the site shares a single exponential ease-out, cubic-bezier(0.22, 1, 0.36, 1), so the whole page decelerates like one machine. There are only three motion materials: masked-line headline reveals on load, scroll-linked transforms (the photo rail slides, the spine stitch draws with a spring), and crossfades when the colour card re-dyes its panel. Nothing bounces. Everything honours prefers-reduced-motion with a static alternative.
06 · Process
Three fine-toothed passes before anyone saw it
After the first complete build, the site went through three full iteration passes in a real browser: screenshots at desktop and mobile widths, console checks, contrast measurements, and a hunt for anything that read as template output. Each pass produced a fix list; each fix list was applied before the next pass. The rule: the site is not done when it works, it is done when the third pass comes back quiet.
If you build your own version, keep the order: metaphor first, palette from the product, type from what you own, motion on one curve, then iterate with your eyes and not your assumptions.