Skip to content
Ritesh FirodiyaGet in touch

Work / Tic Tac Toe / Wiki / Decisions

2026-09-22-design-peripherals-standardised

Decisioncanonicalverified 2026-09-22

DECISION.2026-09-22.DESIGN-PERIPHERALS-STANDARDISED

Decision

This set adopts the estate-wide design peripherals: one surface folder per surface, the six-section gallery, the generated breadcrumb and surface switcher, the closed variant vocabulary, and a single hand-authored shared.css.

Why

Nine products had drifted into two incompatible shapes, and a reader who had learned one set could not navigate another. The UX is deliberately per product; everything around it should not be.

This set was migrated first because it is the smallest, so the pattern could be proven on seven screens before being applied to chitragupt's 269.

Impact

  • routes.js added: the one place a screen is declared. The gallery, the chrome bar and the screenshots sheet all read it, so the screen order cannot disagree with itself any more.
  • One screen per file. The theme is now a toggle in the chrome bar rather than a second frame — a frame has no URL, so it could not be linked, listed or listed on the screenshots sheet.
  • No prose in any screen file. The rationale moved to .context/wiki/surfaces/*.md and is rendered beside the screen on demand by the notes drawer, collapsed by default.
  • The frame is scaled to the viewport. 390×844 plus a chrome bar is taller than a laptop screen, so every screen previously needed scrolling to show its own bottom.
  • core.css, tokens.css and local.css folded into one shared.css. They carried GENERATED — DO NOT EDIT headers pointing at a generator that has been deleted; the headers were lies the moment it went.
  • home/index.html → home/start.html — no index.html inside a flow.
  • states/ads.html split into store/banner.html, store/banner-empty.html and store/ads-off.html; states/ is reserved for 404/500/offline/maintenance.

Two states very nearly lost

Splitting the side-by-side frames, the second frame looked like a dark-theme duplicate and was dropped. It was not. board.html's second frame was "Computer is thinking" — the app's only loading state — and result.html's was the draw outcome. Both were restored from git as game/board-loading.html and game/draw.html, and result.html became win.html so the pair is named for what each is.

The lesson is in the standard now: a second frame is only ever a theme when you have read it, and two of three were carrying real content.

Two factual errors corrected

  • The wireframes cited src/lib/ads.ts. Ads live in src/config/, the interstitial in src/hooks/. The path has never existed.
  • CLAUDE.md claimed banner-only monetisation while useInterstitialAd.ts ships and src/app/game.tsx imports it.

One bug in the theme scope

.phone--dark remaps the tokens, but text with no colour class inherits the computed colour from body, resolved in the light scope — so "Your turn" (and anything like it) stayed black in a dark frame. .phone now sets color: var(--ink) itself. A variable remap only reaches declarations that read the variable; inheritance carries the old value, not the new name.

Status

Active.

Sources

  • ~/git/products/CLAUDE.md — the standard

Every project of mine is written down like this.

Read the résumé