Makers

The making of this site

How this site was woven

Every pixel here is drawn in code — the loom in the hero is a fragment shader, the icons are single threads of SVG, the section edges interlock like cloth. This guide explains how, honestly and completely, so another designer could weave their own.

Creative direction

The page is a loom

Makers Intelligence serves seamstresses, weavers, braiders — people whose work is literally made of thread. So the metaphor is not decoration; it is the layout system. The page composes as horizontal strips, like cloth coming off a kente loom: ivory strips for reading, full-bleed emerald strips for chapters that need weight, and gold only ever as a thread running through — never as a slab.

Even the transitions between sections obey the metaphor: strip edges interlock with rounded warp-thread tabs instead of meeting in a straight line. Scroll the home page slowly and you can watch each band being woven into the next.

The palette is cut from the same cloth: deep emerald grounds, warm gold wefts, ivory for air, and a clay accent used the way a weaver uses a contrast thread — sparingly, on purpose.

Deep emerald#0A3B2E
Forest#0E4D3A
Warm gold#C9973B
Gold light#E3B65C
Ivory#F7F2E9
Clay accent#B4552D

Type: one serif, worked hard

Fraunces is a variable font with an optical-size axis, which means the same family can be a soft, wonky display face and a sharp small-caption italic. We push that axis to both extremes on purpose — it is the typographic version of thick and thin thread from one spool. Archivo carries the interface and body text underneath it.

Fraunces · opsz 144 · display held. hero headlines — high contrast, confident
Fraunces italic · opsz 9 · captions drawn entirely in code, like everything on this site captions — soft, low contrast, bookish

Brand identity

A monogram made of thread

The mark is an M whose uprights are the warp — the vertical threads a loom holds in tension — and a single gold weft passing across it: over the uprights, under the diagonals, the way plain weave actually interlaces. The wordmark sets “Makers” in Fraunces with the ™ tucked high, like a maker’s label stitched into a hem.

WARP · THE M, HELD IN TENSION WEFT · ONE GOLD THREAD, OVER AND UNDER
The weft breaks where it passes “under” the diagonals — the gaps are the weave.

Six services, one continuous thread

Each of the six things Makers carries gets an icon drawn in the same voice: a single stroke weight, round caps, no fills — as if each icon were bent from one length of wire, or sewn in one run of the machine. On scroll they draw themselves in like thread pulled through cloth.

Customers
Orders
Bookings
Follow-up
Content
Growth

The pattern, derived from the mark

Tile the monogram’s grid — warp bars crossed by two gold wefts — and you get the brand pattern used on story-card corners and this page’s header. One drawing, three jobs: mark, motif, texture.

Signature technique

The loom shader, explained

The hero is a single fragment shader on a raw WebGL context — no Three.js, no library, about 8 KB of hand-written code. Every pixel decides for itself whether it belongs to a vertical warp thread, a horizontal weft thread, or the gap between them.

1 · A grid of threads

Divide the canvas into columns and rows about 15–30 px wide. Within each cell, the fractional position gives a thread profile: a rounded highlight via sqrt(1 − x²) so each thread reads as a soft cylinder, not a flat stripe. Colours come from a hash of the thread index, grouped into bands of four so neighbouring threads share a shade — that banding is what makes it read as kente rather than a checker.

2 · Plain weave, one line of math

Real cloth interlaces: each warp goes over one weft, under the next. In a shader that is a parity test — and shading whichever thread goes “under” with the other thread’s curve makes the interlace read as depth:

// column ci, row ri — who is on top at this crossing?
float warpTop = mod(ci + ri, 2.0);
if (warpTop < 0.5) {
  c = mix(c, warpC * (0.62 + 0.38 * abs(wpy)), warpA); // warp dips under
  c = mix(c, weftC, weftA);
} else {
  c = mix(c, weftC * (0.62 + 0.38 * abs(wpx)), weftA); // weft dips under
  c = mix(c, warpC, warpA);
}

3 · The shuttle weaves it in

On load, only the warp hangs there, dim and slack. A progress uniform sweeps a “weaving front” down the canvas, and each weft row switches on as the front passes — alternating direction row by row, the way a shuttle actually travels. Rows above the front brighten as the cloth tightens:

// rTop: row index from the top · sx: position along the row,
// flipped on alternating rows like a shuttle pass
float t      = u_weave * (rowsTotal + 10.0) - rTop;
float sx     = mix(uv.x, 1.0 - uv.x, mod(rTop, 2.0));
float weftOn = smoothstep(0.0, 0.22, t - sx);

4 · A hand near the cloth

The cursor doesn’t push pixels — it bows the threads. Sample positions are displaced away from the pointer with an exponential falloff (exp(−distance × 3.2) × 9px), so the cloth seems to dimple around your hand, and a faint gold shimmer follows like lamplight on silk. The pointer is smoothed with a simple lerp so the cloth feels heavy, not jumpy.

What it costs, and the fallback

  • Device pixel ratio is capped at 2; the render loop pauses when the hero scrolls off-screen or the tab hides.
  • Under prefers-reduced-motion — or wherever WebGL is unavailable — the canvas is never started; a static woven-cloth SVG pattern takes its place. Same cloth, at rest.
  • The whole thing costs one draw call of a full-screen triangle per frame.

Layout system

Section edges that interlock

Between every pair of bands sits a thin strip whose background is the band above, with the band below rising through it as rounded tabs — a CSS mask-image holding a tiny inline SVG of four staggered warp threads, repeated horizontally. Two custom properties tell each strip which colours it is joining:

/* <div class="weave-edge" style="--above:…;--below:…"><i></i></div> */
.weave-edge     { height: clamp(22px, 3.4vw, 40px); background: var(--above); }
.weave-edge i   { position: absolute; inset: 0; background: var(--below);
                  mask-image: url("data:image/svg+xml,…four rounded rects…");
                  mask-size: 96px 100%; mask-repeat: repeat-x; }

It costs two elements and zero JavaScript, works at any width, and quietly carries the thesis: the sections aren’t stacked, they’re woven.

A lesson we learned

Drawing icons through the shadow DOM

The icons live once in the document as <symbol> definitions and are placed with <use>. Our first build styled them with .t-icon path { stroke: … } — and in the first design-critique screenshots every icon rendered as a solid black blob. The reason is worth knowing: CSS selectors cannot reach inside a <use> element’s shadow tree, so the paths never got their strokes; they fell back to SVG’s default black fill.

The fix is to stop selecting the paths and instead set inherited SVG presentation properties on the host <svg>fill, stroke, stroke-dasharray and friends all cascade through the shadow boundary:

.t-icon          { fill: none; stroke: currentColor; stroke-width: 2.4; }
/* every path carries pathLength="1", so one rule draws any icon: */
.js .t-icon.draw     { stroke-dasharray: 1 1; stroke-dashoffset: 1;
                       transition: stroke-dashoffset 1.6s var(--ease-out); }
.js .t-icon.draw.in  { stroke-dashoffset: 0; }

pathLength="1" is the quiet hero: it normalises every stroke to the same unit length, so the dash math never needs to know how long a path really is. When an icon scrolls into view, an IntersectionObserver adds .in and the whole icon sews itself in.

Toolchain & pipeline

How it was actually made

This site was designed and art-directed by Hannah Kwakye and engineered with Fable 5, Anthropic’s frontier model, working as a designer-engineer under direction — writing the HTML, CSS, GLSL and JavaScript by hand, taking screenshots, critiquing them, and iterating. No page builder, no framework, no template.

  • Static, hand-authored HTML/CSS/JS. Zero libraries, zero runtime requests to third parties. The interactive machinery is IntersectionObserver, a rAF loop, and one WebGL context.
  • Self-hosted variable fonts. Fraunces (upright + italic) and Archivo, subset to woff2 at build time and served from /assets/fonts — never from Google’s CDN. The display font is preloaded.
  • Every visual is code-drawn. That began as an environment constraint — no stock photos, no AI-generated rasters — and became the creative thesis of the whole collection: if you can’t borrow warmth, you have to build it. The loom, the map, the dashboard, the icons: all SVG, CSS and shader.
  • Netlify, CI-driven. Push to deploy; immutable caching on /assets/*; security headers in netlify.toml; the waitlist runs on Netlify Forms with a honeypot field and an inline success state.

Accessibility & performance, concretely

  • Semantic landmarks, one h1 per page, a skip link, and :focus-visible styles throughout; decorative SVG is aria-hidden, informative SVG carries real labels.
  • prefers-reduced-motion is a first-class path: the shader never starts, reveals appear without translation, the icon and map draw-ins render complete, and transitions drop to 120 ms.
  • Animations touch only transform, opacity and stroke offsets — nothing that provokes layout. Parallax is capped at ±40 px.
  • First view is about 200 KB transferred, all three font files included — the shader hero costs kilobytes, not megabytes, which is the point of writing it raw.

Iteration protocol

Three passes, logged honestly

Every site in this collection goes through the same discipline before it ships: screenshot everything at three widths, critique it as if it were someone else’s work, act on the findings — then push two things further, then hunt down whatever is broken. Here is what each pass actually changed on this site:

  1. Pass i. Design critique

    • The thread icons were rendering as solid black fills — CSS can’t select into <use> shadow trees. Restyled them with inherited presentation properties on the host SVG (the fix documented above), which also made the draw-in animation actually work for the first time.
    • The hero eyebrow was set in clay red on the dark loom — handsome on ivory, illegible on emerald. Switched it to light gold and deepened the scrim’s left side so the lede clears the brightest weft band.
    • Fast scrolling could shoot past a section before its reveal fired, leaving cards invisible; the observer now also reveals anything already scrolled past, so no viewer can outrun the loom.
  2. Pass ii. Elevation

    • The hero headline now rises word by word out of the weave — each word masked in its own clipping span, staggered 75 ms apart, skipped entirely under reduced motion.
    • The “Born in Ghana” map gained weft packets: gold shuttles that travel the arcs from Accra outward once the threads have drawn in — SMIL motion started by JavaScript only when motion is allowed.
    • The workroom dashboard’s sparkline now weaves itself in when the section arrives, endpoint blinking on last — a small thing, but it makes the mock feel alive without a single fake metric changing.
  3. Pass iii. Ship quality

    • Every route, anchor, form state and cross-link tested; the waitlist form verified with its honeypot, inline success message, and ?success return path.
    • Zero console errors at every width on all three pages; reduced-motion verified with the shader replaced by the static cloth and all draw-ins pre-completed.
    • Proofread every word — including the GLSL comments — and confirmed the pre-launch honesty notes (illustrative personas, founding pricing) survive on every page that needs them.

See it on the loom

The best way to read this guide is with the site open beside it — watch the bands interlock, bow the threads with your cursor, and see the icons sew themselves in.

Visit the site Read the design process