Cowichan Souvenir Co.

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.