build handoff · decisions & deploy

what we built & how to keep it

A durable companion to the design system: the files, the decisions behind them, the deploy path, the font wiring, and the traps to avoid. Written so a future session — or you — can pick up cleanly.

last updated 29 jul 2026 · flip ☀/☾ top-right

Open the design system
01

the files

Everything lives in the artifacts folder and persists across sessions. The two that matter for production are the first two; the rest are mockups kept for reference.

work-with-me-v2 production

The finished Work With Me page. Deployed to prototypes.zaheermerali.com/artifacts/_stealth/work-with-me/. Carries the full verbatim copy plus the glass nav, footer, themes, gallery, infinity loop, and water droplet.

design-system source of truth

The living reference: themes, tokens, type, ◐ glyph, glass nav + menu, motion vocabulary, components, hanging canvases, the infinity loop, water, and The Mirror (§11). Kept in sync with the production pages.

clarity-perspective/the-mirror production · generated

The reflections index + article pages, generated from Notion by sync/sync-the-mirror.mjs. Shares article.css, article.js, themes.js, icons.js, theme-toggle.js. Do not hand-edit the generated *.html, images/, or icons/ — re-run the sync. Workflow: sync/ADDING-A-POST.md.

arc-infinity

Standalone mock of the infinity motion. Now folded into both pages.

nav-system

Original glass nav + footer mockup.

type-specimen

The trio + serif candidate study (Newsreader won).

glyph-menu · pivot-sweep

◐ lockups and the menu-button animation studies.

02

the decisions

03

deploy path

The prototypes host is a static Cloudflare Pages site. Drop the file into an audience folder, run the deploy script, and it goes live behind Cloudflare Access.

  1. Copy the finished file into the stealth folder:
    cp ".../work/artifacts/work-with-me-v2/index.html" \
      ~/Desktop/zaheermerali.com/prototypes-host/artifacts/_stealth/work-with-me/index.html
  2. From the repo root, run ./deploy.sh — it regenerates the index, commits, and pushes. Cloudflare Pages auto-builds.
  3. Confirm at prototypes.zaheermerali.com. Quick check for "latest": scroll to the Arc section — if the ◐ traces the infinity loop, it's current.

Audience = folder. _stealth/ is you-only via Cloudflare Access. Display name comes from the page <title>.

04

font wiring

The page links fonts directly (it keeps its own CSS; it does not import the host's LUMA stylesheet, to avoid style bleed).

later, optional

The Nexa files are .otf (heavier than woff2). If Nexa ever feels slow in production, convert those two files to .woff2 and update the two @font-face src paths.

05

traps & gotchas

Two bugs cost real time. Both share one lesson: don't let theme tokens flow into a surface that isn't theme-aware.

the token cascade trap

Nav tokens like --nav-ink: var(--ink) are declared on :root (the html element). Overriding --ink on body.dark does not reach them, because --nav-ink resolves its var(--ink) at the html level — where it's still the light value. Fix: re-declare --nav-ink, --nav-ink-soft, --nav-accent explicitly inside body.dark.

fixed surface vs theme text

The paintings/canvases are always warm-paper light. When their text used var(--ink), it turned cream in dark mode → invisible on the light painting. Rule: text on a fixed-light surface stays fixed dark ink; text on a theme-aware surface follows the theme. (We later made the panels fully theme-aware, which removed the tension.)

process note

Always verify rendered output against the design-system page before claiming a match — copying CSS source and assuming parity missed both bugs above.

The Mirror added a few more, worth keeping:

make the theme visible, not just correct

Per-article --tint was applied correctly but nearly invisible — it drove only ~5 rare elements while --accent (theme-neutral gold) drove ~26 prominent ones, so every article looked the same. Fix: the theme drives the whole accent chain (applyTheme sets --accent/--nav-accent/--nav-hairline) and tints large surfaces (page wash, reading panel, water). Lesson: a token only differentiates if it touches a large, prominent surface.

IntersectionObserver rootMargin needs units

rootMargin:'-70px 0 0 0' throws a SyntaxError that aborts the whole script (the dock + water silently died). Must be '-70px 0px 0px 0px' — every value carries px or %.

reuse the engine, don't reinvent it

The reflective pool is the design-system water engine verbatim (Mirror variant just changes source + cadence + tint). An earlier improvised two-canvas drip had mismatched coordinates and never landed. Lesson: lift the working code, parameterize the difference.

keep the three icon maps in lockstep

Icon keys must match across the sync's POST_ICON, the download list, and icons.js's inline LUCIDE fallback — all real Lucide file names. When they drifted, every icon fell back to the ◐ glyph. Icons hydrate self-hosted-first, so a missing/renamed file silently degrades.

generated files are disposable

Everything under the-mirror/*.html, images/, icons/ is written by the sync — hand-edits are lost on the next run. Change the templates (sync/templates/), the shared assets, or the registries instead.

06

open items