Build Guide — How this site was made
Drawing a maison in code
DUAFE Atelier is the collection's pure art-direction statement: a couture house with zero photography. Every look is a hand-drawn SVG croquis; every moment of motion is vanilla JS. This page explains how — generously enough that you could rebuild it.
Concept & creative direction
Couture houses sell photographs of clothes. The brief inverted that: present a couture collection the way couturiers actually work — as the atelier sketch. That single decision set every other rule.
- Three inks, nothing else. Off-black
#141210is the paper, bone#EDE6DAis the thread, oxide red#9E2B1Eis the one seam the eye should land on. There is no fourth color anywhere on the site. - Two voices. Bodoni Moda — high-contrast, editorial, allowed to be enormous — speaks for the house. Archivo Narrow, wide-tracked and uppercase, is the caption voice: folios, fabrics, footnotes.
- Magazine structure. Cover, chapters, folios (Nº 01–07), a masthead credits column, and a lookbook that reads sideways like a runway.
The constraint — no photography, no renders, no stock — is not a limitation the site apologizes for. It is the house's founding myth ("drawn, never photographed") and the collection's thesis: everything visible is authored in code.
Toolchain
The site was designed and engineered by Fable 5 (Anthropic's model) working as designer-engineer, directed by Hannah Kwakye — art direction, taste and final cut are hers. The stack is deliberately austere:
- Hand-authored static HTML, CSS and JavaScript. No framework, no build step, no bundler.
- Zero libraries. The lookbook drag, the self-drawing croquis, the silk filter and the reveals are IntersectionObserver, pointer events and requestAnimationFrame — about 240 lines of vanilla JS in total.
- Two self-hosted variable fonts (Bodoni Moda upright + italic, Archivo Narrow) as woff2, preloaded,
font-display: swap. No runtime request leaves the origin. - First-view transfer is roughly 175 KB before compression, fonts included — most of it type, because type is the photography here.
Everything decorative — the favicon comb, the Adinkra duafe in Chapter III, the cover thread, the seven looks — is inline SVG, so the entire visual identity ships inside the documents themselves.
Drawing the seven croquis
Each look is a hand-plotted SVG on a shared viewBox="0 0 360 640" stage — the proportions of a tall fashion croquis. The drawing discipline is strict, because confidence is the whole aesthetic:
- One primary stroke weight (2.4 at viewBox scale) for the garment's silhouette; a
.thinclass (1.5) for interior fall-of-fabric lines; a.redclass for seams. Round caps and joins everywhere — a pen, not a router. - Fabric is suggested in three or four sweeping cubic Béziers, never modeled. Noon Haze's twelve metres of gazar is literally four curves.
- One red statement per look. A waist seam, twin column seams, a slit — the oxide line is rationed so it keeps its authority.
The self-drawing effect is the classic dash trick, primed per-path so the CSS stays declarative. On load, JS measures every path and stores its length in custom properties; a single class added by an IntersectionObserver runs the whole choreography:
svg.querySelectorAll('path').forEach((p, i) => {
const len = Math.ceil(p.getTotalLength()) + 2;
p.style.setProperty('--l', len); // dasharray
p.style.setProperty('--o', len); // dashoffset
p.style.setProperty('--i', i); // stagger index
});
.look.drawn .croquis path {
stroke-dashoffset: 0;
transition: stroke-dashoffset 2s var(--ease-runway);
transition-delay: calc(var(--i) * .16s);
}
Because paths are written in drawing order — head, neckline, bodice, skirt, seams — the stagger makes each look sketch itself the way a couturier would: figure first, red last. Under prefers-reduced-motion the dash properties are never set, so every look is simply, instantly drawn.
The horizontal lookbook
Chapter I reads sideways, like a runway. The mechanism keeps one source of truth — page scroll — which is what makes it robust:
- The wrapper section is given a height of
trackWidth − viewportWidth + viewportHeight. Aposition: stickystage pins to the viewport while the wrapper scrolls by. - Every frame, progress
p = (scrollY − wrapTop) / spanmaps totranslate3d(−p·span, 0, 0)on the track. Scrollbar, mouse wheel, keyboard, drag — all agree, because they are all just scroll. - Momentum drag converts a horizontal gesture into that same scroll: pointer
dxbecomesscrollBy(0, −dx), a low-pass filter tracks velocity, and on release an inertia loop decays it at 0.94 per frame — the runway keeps gliding after the hand lets go.
function inertia() {
if (Math.abs(vel) < 0.4) return;
scrollBy(0, vel);
vel *= 0.94;
requestAnimationFrame(inertia);
}
Below 820px the same DOM relaxes into a vertical editorial: the sticky stage becomes static, the track becomes a column, and the drawings trigger per-look as they enter the viewport. No second markup, no JS branch beyond a media-query check. Arrow keys also step the lookbook look-by-look while it is on screen.
The silk-distortion type
The manifesto opens with the house motto — Drawn, never photographed. — set as SVG text and displaced through an animated turbulence field, so the words hang and breathe like silk on a form:
<filter id="silk">
<feTurbulence type="fractalNoise"
baseFrequency="0.011 0.045" numOctaves="2" seed="7"/>
<feDisplacementMap in="SourceGraphic" scale="0"
xChannelSelector="R" yChannelSelector="G"/>
</filter>
A rAF loop eases scale between 2 and 14 on a slow sine and drifts baseFrequency even more slowly, so the cloth never repeats itself. The anisotropic frequency (wider horizontally than vertically) is what makes it read as draped fabric rather than heat-haze. The loop only runs while the manifesto is on screen (IntersectionObserver-gated) and never runs under reduced motion — the filter simply stays at scale="0", which is plain, sharp type.
Iteration log
The collection's protocol: three passes, screenshots read like a creative director, findings acted on. What actually changed:
Pass 1 — Design critique
- The cover title HARMATTAN was clipping glyph stems at both viewport edges — a vw-guessed size can't know the font's real advance widths. Replaced it with a measure-and-refit routine so the word now spans the viewport exactly, edge to edge, at every width.
- Look Nº 03 Laterite read as a bowling pin: its "high collar" was an illegible dome under the chin. Redrawn as a true funnel collar with a shoulder yoke and cleaner entasis on the column.
- Look Nº 05 Ember was symmetric, so the one-shoulder story vanished. Redrawn with the neckline sweeping from the left underarm over the right shoulder, and the slit seam moved into the flare where it belongs.
- The appointment form's occasion select had no affordance; it now carries a small drawn oxide chevron. Look Nº 06 gained two thin strap lines so the gown no longer floats detached from its head.
Pass 2 — Elevation
- Gave the seams a hand: on hover (pointer-fine only) a look's oxide seam lines thicken and its folio numeral brightens — the red answers the cursor the way a seam answers the needle.
- Made the runway keyboard-native: ← and → step the lookbook to the previous/next look while Chapter I is pinned, with the same runway easing as the drag.
- Extended the silk turbulence's travel (scale 2→14) and slowed its drift after side-by-side screenshots — the earlier setting read as jitter, the new one reads as cloth.
Pass 3 — Ship quality
- Caught a font-data bug: this Bodoni Moda build's optical-size axis collapses dash glyphs (- – —) to empty outlines and grows ghost contours above roughly opsz 14 — em dashes were vanishing from running text. The opsz axis is now pinned to the clean text cut site-wide.
- All three routes screenshotted at mobile/tablet/desktop with zero console errors; every nav, footer and in-page link resolves; the Netlify form posts with honeypot and inline
?successhandling verified. - Reduced-motion pass re-verified in emulation: croquis render fully drawn, the silk stays flat, reveals appear without travel, and the lookbook still paginates.
- Fonts confirmed self-hosted and loading (three woff2 files, ~119 KB total); first-view transfer ~175 KB; every word proofread against the three-ink, two-voice system.
Deploy
The site is static files on Netlify, deployed CI-style from the collection's repository. netlify.toml publishes the site root, sets immutable year-long caching on /assets/* (fonts and CSS are content-stable), and adds the standard hardening headers. The appointments form is Netlify Forms — a plain HTML <form data-netlify="true"> with a honeypot field, no JavaScript required to submit.
This is one of 26 sites in Hannah Kwakye's collection — the shared thesis being that AI doesn't replace taste; it amplifies the designer who has it. See the design process for the thinking, or return to the collection.