Skip to content

Wireframes System

Status: Active

One config-driven wireframe per app section — real brand tokens, static-rendered (no CDN), deployed under /wireframes on the Cockpit. This is the design surface for aligning layout, hierarchy, and flow before app code. It is deliberately separate from the interactive ui_kits/mobile click-through kit.

The one rule: wireframes are grounded in the real code

The real components under packages/… are the single source of truth for what a screen is. Everything else — screen-blueprint.md, the kit *Screen.jsx mocks, these wireframes — is a subordinate representation that must track the code.

A wireframe built from a doc or a mock instead of the code caused a real drift incident: the Home wireframe was drawn as a "10-card feed" (a superseded design the screen-blueprint still described) when the app actually ships a 오늘/이번 주/이번 달 temporal briefing. The tripwire below exists so that can't happen silently again.

Build a section wireframe by READING the real components it mirrors — trace what the route actually renders (e.g. home-feed-screen.tsx → its views → SessionCard), not the obvious-named doc. See research-methodology.

The drift tripwire

Every section in scripts/build-wireframes.mjs declares the real files it reflects:

js
{ key: 'home', file: 'wireframes/home.jsx', component: 'HomeWireframe',
  groundedIn: [
    'packages/features/home/src/home-feed-screen.tsx',
    'packages/app/src/presentation/components/sessions/session-card.tsx',
    // …
  ] }

On every build (yarn docs:wireframes, and docs:site:build inside docs:check):

  • Hard-fail if a cited source no longer exists (moved/renamed ⇒ the grounding is broken). This fails the pre-push + CI gate.
  • Warn if a cited source's content changed since the wireframe was last grounded — a prompt to re-verify the wireframe still reflects it.

The committed baseline of source hashes is .grounding.json. After re-verifying a board phone by phone against the real screen (list what the real screen renders top to bottom, list what the phone draws, diff them), accept the baseline for THAT board only:

bash
REGROUND=home,notifications yarn docs:wireframes

REGROUND=1 (accept everything) exists only for a first baseline. It is what let stale boards slip through on 2026-09-06: drift was accepted because the source files had changed in the same session, which is provenance, not verification. The owner then found the boards did not reflect the app. Provenance never justifies acceptance; a diff of phone versus screen does.

The four lanes

Every section carries a status in scripts/build-wireframes.mjs's SECTIONS array — 'current' | 'proposal' | 'decided' | 'superseded' — and that one field drives both the hub (/wireframes) and the top-bar Screens menu, which render the whole map as four lanes, in this order:

  1. For reviewstatus: 'proposal'. Open decisions the owner hasn't made yet. Each proposal declares open: ['…'] (short strings) — the specific things still undecided — and the hub lists them under the board's blurb, unresolved. Approved — build queuestatus: 'approved' (+ approvedDate). The owner accepted the recommended path; the board is the build spec until its screens ship, then it flips to decided. Rendered between For review and Foundations only when non-empty.
  2. Foundations — a fixed, hand-picked set (components, detail-flat-language, session-card): the shared kit + the cross-cutting canon boards every other screen composes from. This is NOT derived from status — notably detail-flat-language is status: 'decided' (ratified 2026-09-05) but stays here as the living spec rather than moving to Archive, because every other board still actively cites it as the current flat-page grammar.
  3. Screens — the real app navigation cascade (NAV, rooted at the 5 bottom tabs), pruned to status: 'current' nodes only. A non-current ancestor (a proposal, a decided/superseded board) is skipped without dropping its still-current children — they're promoted up so nothing shipped is ever hidden behind an undecided parent. A board with stale: true (its drawn grammar no longer matches the code, even though its content is otherwise current) carries a "stale — being redrawn" tag; it stays in this lane, it is not re-accepted blindly, and it is not archived — it needs a redraw, not a decision.
  4. Archivestatus: 'decided' or 'superseded' (minus whatever's already in Foundations), collapsed by default. A decided board is tagged "shipped decidedDate" — it's the historical decision record, kept for context, not a live spec. A superseded board is tagged "superseded → <successor title>" via its supersededBy key, and its file moves to archive/ (see below) — it's dead weight kept only so the decision trail isn't lost.

The rule going forward: a superseded board moves to archive/ with supersededBy pointing at what replaced it; a proposal lists its open decisions so a reviewer sees exactly what's blocking it; a stale board is redrawn against the current code, never re-accepted (REGROUND=1) blindly just because the build warns. Every built page also carries a config-driven banner matching its lane — "For owner review — open: …" on a proposal, "Decided … — shipped; kept as the decision record" or "Archived — superseded by …" on a decided/superseded board — so the status is visible on the page itself, not only on the hub.

Adding or updating a section wireframe

  1. Read the real code the section renders (route → screen → components). Note the actual structure, section names, card anatomy, and states.
  2. Write/edit wireframes/<section>.jsx — one config object → components, reusing the shared ui_kits/mobile/Primitives.jsx + _kit.jsx (WF* primitives + tokens) + colors_and_type.css tokens. Export the component on window.
  3. Add/update the section entry in scripts/build-wireframes.mjs, including groundedIn (the files you just read) and a status — pick proposal if the owner hasn't decided, current once it ships (add stale: true later if the code drifts out from under the drawing without a redraw yet), decided once a proposal is ratified (add decidedDate), or superseded once something else replaces it (add supersededBy: '<the successor's key>' and git mv the file into wireframes/archive/, updating its file: path — the URL/key never changes, only where the source lives).
  4. yarn docs:wireframes → verify the render + the grounding manifest + that the section landed in the lane you expect on the hub. Deploy with the Cockpit (yarn docs:site:buildwrangler pages deploy).

Sections

SectionFileStatus
Home · 홈home.jsxFaithful to home-feed-screen.tsx
Clubs · 클럽clubs.jsxConsolidated (2026-08-01) — every club surface, one section. ui_kits/mobile/ClubSurfaces.jsx deleted (superseded, was never wired into any build).
Sessions · 경기Planned
Records · 기록Planned
Profile · 프로필Planned

The full, current status of every one of the 40 sections lives in scripts/build-wireframes.mjs's SECTIONS array (the single source of truth) and on the hub (/wireframes) — this table is a historical sample, not maintained per-section; don't hand-update it when a status changes elsewhere.

Markdown remains the source of truth. Run yarn docs:check before handoff.