Skip to content

Owner review 49 — the patterns in the page review (2026-10-30) ​

Status: Draft · Date: 2026-10-30 · The owner marked every page from L0 to L4 on the page review (https://claude.ai/artifact/EKvHgpfADndkXvjNPhRQHF): 84 pages, 66 fine, 14 to fix, and notes on 22. Then: "I've completed the review, but the review process itself is hardly done. Please thoroughly examine my comments, consolidate them, and apply audits and fixes in a foundational, shared and sound engineering practices way. We are trying to make sure all of the pages follow best practices." This record groups the notes into patterns, audits each pattern on every page with evidence, and fixes each at the level it comes from. Companions: review 48, the research, the library's § 1.6, § 1.18 and § 1.21.

How this was investigated ​

  • The notes. Every mark, note and earlier note on the review page (84 reviews documents) and every answer given (79 replies).
  • The pages, measured. A rhythm probe rendered all 1,332 frames of the six page boards and recorded every page block top to bottom with what it paints, then the gap between each pair of neighbours. Rows, Cells and chrome are measured by their box (a row's padding is its rhythm), a Section head by its title line, anything else by what it paints. 1,298 frames at 100% text, 241 kinds of neighbouring pairs.
  • The app. Where a note names a part: the offline banner's mount (apps/mobile/app/_layout.tsx, network-banner.tsx), the club logo generator (packages/features/clubs/src/create-club/step1/decor-section.tsx, packages/ui/src/flag-medallion.tsx).
  • The outside. Two research briefs with sources (research): tournaments and live play (ATP, the New York Grand Slam, WTA, Toornament, a Korean tournament app); the home feed's lifecycle, the trust meter and per-row actions.
  • The review tool. The page review lost the end of the L3-38 note ("This is getting better, but we", and on 10-28 "Find best pr"). A finished save cleared the page's unsaved flag while the next save was still waiting, the server's echo then put the older text back in memory, and the waiting save sent it. Fixed: the note is read from the box at save time, the flag clears only when nothing is waiting, the echo never touches a note with a save waiting, and hiding or leaving the page sends every waiting note.

1. The direction in one paragraph ​

Spacing, position and composition belong to the system, never to a page. Every gap between two page blocks is one value in one canon, and a block that appears on demand takes the same space above and below; the verifier fails anything else, so a new component in a new place gets its seam decided once, in the canon. App chrome (the offline strip) has one place, drawn by the frame, as the app mounts it once. Where a decision differs per person, the action sits on that person's row. A question asked on two pages is asked with one shared composition (when, where), and the app's working parts lead (its logo generator). Live play is a Block with its state inside it, and a tournament reads as one connected set: the draw, the order of play, the live matches and the results, with one match card everywhere.

2. The patterns ​

S1 — Spacing is the system's, not the page's ​

The owner: "Whenever we introduce an on-demand component in the middle of two components that are appropriately spaced, we need to make sure there is still equal and canonical spacing above and below ... If there is a reason this is a recurring issue, please fix it at the foundational level so that it cannot occur even if we introduce other components in the same position" (L1-03); "I keep having to correct you on spacing whenever we introduce new components" (L2-01); "Do you think it's possible there's too much space above the venue title ... why don't you do a thorough spacing/alignment pass on all pages ... We have a system for spacing and alignment. Please use it" (L2-02); "Spacing issue persists" (L3-39); "I hope our spacing/alignment pass is applied to all of the pages in L4 as well" (L4-01); "Another spacing pass, please" (L4-04).

Why it recurred (the finding). The ladder (§ 1.3) and the seams (§ 1.6) were rules on paper, and every page applied them by hand: the six page boards place about 390 spacers of their own, and kit pieces carry their own outer space (the Offline strip 8 above and below, a Section head 32 above). A piece inserted between two others inherits whatever its neighbours bring: on 매치 찾기 the offline strip had 8 above and 32 below. Nothing checked it: the board verifier has no spacing check, and it runs in no gate.

Measured. 395 gaps off their pair's usual value on 59 pages, and 119 on-demand blocks with unequal space above and below. Examples: tabs to the first head 26 on 72 frames and 14 on 14 (클럽 홈); the map band to the hero's title 49 on 일정 상세, 매치 미리보기 and 장소 상세 where the ladder says 24 (the owner's "too much space above the venue title"); a Cell group to the next head 37 on 214 frames and 30 on 128; a Notice under a pane's Tabs 16 above and 34 below (테니스장, location denied); the filter Choice row 14 above and 18 below (매치 찾기).

The fix, at the root.

  • The rhythm canon (scripts/lib/flat-v2-rhythm-canon.json, library § 1.6): one gap per pair of page blocks, 177 pairs, taken from the system as drawn and corrected where the drawn value broke the ladder (the hero's title 24 under its band; the first Cell 8 and the first row 4 under a Header). It is the spacing system as data.
  • The insert law: an on-demand block (a Notice, the filter Choice row, or anything a page marks data-insert) has the same space above and below, and that space is a step of the ladder.
  • The verifier enforces both (rhythm and insert, scripts/lib/flat-v2-verify-rhythm.mjs, each with its canary). A pair the canon does not list fails, so a new component in a new position cannot pass until its seam is decided in the canon, once, for every page.
  • The verifier joins the gate for board changes, so the check runs whether or not someone remembers it.
  • Every page is fixed to the canon (all 59 pages with findings).

S2 — App chrome has one place: the offline strip ​

The owner: "these alert bars are positioned in different places even though they are both on-demand components. We should aim to streamline this ... and establish a consistent grammar that gets across all pages that render these alert bars" (L1-03c).

Finding. 45 places on the boards drew the strip themselves, each choosing its spot: under the Tabs on 매치 찾기, above them on 테니스장, attached to the header over a photo on 클럽 홈. The app mounts its offline banner once, in the root layout above every screen (apps/mobile/app/_layout.tsx: "NetworkBanner sits ABOVE the Stack so every screen ...").

Fix. The phone frame owns the slot: a page only asks for the strip, and the frame draws it under the status bar, above the whole screen, 8 above and below, pushing the screen down by its height. No page can place it anywhere else. Notices about the page's own state stay in the content and follow the insert law (S1).

S3 — A decision per person is answered on that person's row ​

The owner: "for pages like these where we have to check and respond to each individual, it might be best not to have a single CTA at the bottom ... in-line buttons at the end of the row ... Please inspect if there are any other pages like this and apply that message" (L3-35); "We should confirm each one's participation, not a large CTA at the bottom" (L3-36).

Inventory. Three pages tick people and then act with one bottom button: 회비 관리 (L3-11, 확인 대기 with checkboxes), 참석자 관리 (L3-35, "3명 입금 확인"), 참석 확인 (L3-36, "11명 참석 확인"). 멤버 관리's applicants already answer in the row (승인 · 거절).

Fix (library § 1.18). Each waiting row ends in one sm button that names the result (입금 확인, 도착 확인); a real second answer is an answer pair (승인 · 거절). A confirmed row moves to its done group with its own 되돌리기, so several quick confirmations stay undoable (a Toast holds only the last). No checkboxes and no CTA band on these pages. A whole-group command is the group head's quiet control at most (모두 확인), never a CTA; for money it states the count and the amount first. Research: Carbon (inline under three actions; batch only from selection), Slack and Naver Works (per-request answers).

S4 — One question, one shared composition ​

The owner: "Make sure we use shared components (date and time selector, for example). There seem to be many instances where we are creating custom components for a page ... This is a shared component, single source of truth pass on all pages. And users should be able to input a location directly inside the 장소 page using an external API like Naver" (L4-03).

Finding. No page board draws a control of its own (a sweep of every page-local component found none). The drift is one level up: the same question gets a different kit composition per page. 일정 만들기 asks when with one 일시 Cell (the shared date picker with the time range), 행사 만들기 with two (날짜, 시작 시간). Where is a search over the registered venues and then Naver on 일정 만들기, a free-text Input on 행사 만들기.

Fix (library § 1.21, Questions). A table of the app's recurring questions and the one composition each uses: when (the 일시 Cell), where (the place search: registered venues first, then Naver), who (the people picker), how much (the fee band), level (the NTRP band). Forms use the table's composition; 행사 만들기's 장소 searches places through Naver, as any place can host a 행사.

S5 — The app's working parts lead: the logo generator ​

The owner: "I do hope you will include the logo generator we worked so hard to implement in our implemented version of the app" (L4-02).

Finding. The app's 클럽 만들기 step 1 has a full generator (decor-section.tsx, FlagMedallion): 대표 색상 (a hue slider), the 로고 fork (로고 업로드 · 문양 사용), the monogram input with its counter, 글자 크기 (작게 · 보통 · 크게), 모양 (원 · 둥근 사각 · 사각 · 방패), eight 문양 presets and a live club-card preview. The flat v2 page draws only a "자동 문양" row. Fix: 클럽 만들기 draws the generator on the shared controls, with the preview.

S6 — Live play is a Block; a tournament is one connected set ​

The owner: "All ongoing matches should be rendered inside a block to accentuate the live matches. And instead of a meta data on the time elapsed at the bottom, it should be part of the block. And we need the live badge inside the banner and perhaps some flickering around the block ... inspect how grandslams and ATP websites render tournaments so that we can show all relevant information (bracket, match trackers, match results, etc.) about the tournament in a connected and streamlined way" (L3-38; the note ends mid-sentence, see § 6); "make sure these 대진표 are fully researched and equipped with UI/UX to handle various scenarios and stay consistent with how scorecards are rendered" (L3-39); "tournaments need to support brackets and groups and what not" (L3-40, 10-28).

Research (§ 1). ATP's live card carries the round, a filled Live pill, the court and the elapsed time in its own header row. The New York Grand Slam uses one card in the scores list and the bracket. ATP's draw is one round per screen with sticky round chips and a "follow a player" path, and keeps live out of the draw. The order of play groups by court with "Starts at", "Not before" and "Followed by". Motion: WCAG 2.2.2 (auto motion over 5 seconds beside other content needs a pause), so the live signal is a dot that breathes three times when the card appears or changes, then holds, and is static under reduced motion.

Fix. One match card for every live surface, by state (예정, 진행 중, 종료, 기권, 부전승, 늦어짐): a header row with the court and round, the LIVE label and the minutes; the two teams; the result. In play, it is a Block on the live tint. The same card is the console's row (the operator's action rides the card's trailing slot) and the bracket's box. 스코어카드: LIVE (my match first, then each court), 일정 by court with its queue words (12:00 시작, 앞 경기 후), 결과 by round, 순위 (group tables with the tie-break order, the bracket by round with my path). The header's subtitle and the Tag row no longer both repeat the date and the venue.

S7 — The home feed's lanes are by who ends an item ​

The owner: "the feed now mixes items of different priorities. And one should be able to dismiss an item, complete an item, and some items need to stay around (e.g., upcoming or live sessions) to alert the user appropriately. How can we take care of this properly?" (L1-01).

Research. Products separate items by who ends them, not by priority: the system ends a live or upcoming event (iOS Live Activities end with the event and keep a summary for 15 to 30 minutes; Android treats an upcoming event as not live); doing it ends a task (GitHub Done, Linear, Slack Clear); the reader ends news (hide, with an undo).

Fix. Three lanes. The Lead is the system's: the next or live session, never dismissed; while play is on it takes the live look, and after the end it holds the result briefly, then falls into 소식. 할 일 is the viewer's work: no ×, its own action completes it and it leaves when the request is settled; a non-urgent one (가입 신청) may be put off with 나중에. It sits between the Lead and the feed as its own horizontal lane (§ 1.20: a second kind on one line). 소식 is news: each item can be hidden (숨기기 with 되돌리기); reading is not hiding.

S8 — The trust score tells its story ​

The owner: "Can we render 신뢰 in a more progress-bar type deal? The point, 72, alone doesn't really tell much of a story. How can we render it so that it blends in with the rest? 신뢰 score is the central component of the trust model we are building anyway" (L1-05).

Research. 당근's 매너온도 is the Korean precedent: a figure on a bar from a known start (36.5), a face that changes, the counts beside it. Gauges label the value and both ends (Apple); a value in a known range is a meter (ARIA); colour is never the only signal (WCAG 1.4.1).

Fix. One kit piece, the 신뢰 meter: the figure, a 0 to 100 bar with a tick at 50 labelled 시작 (everyone starts there, so 72 reads as above it), and one line of its story (최근 20회 중 19회 참석). One hue, no red-to-green scale. Before five sessions it shows the steps (3/5). It replaces the bare figure wherever the score shows: 프로필, 공개 프로필, 신뢰 기록, 가입 심사.

S9 — A venue shows its own public matches only ​

The owner: "I don't think we need 같은 테니스장의 매치 shown anywhere. Let's only show the ongoing or planned matches on the venue only if 1) they are public and 2) inside the venue detail page" (L2-03).

Fix. 매치 미리보기 drops 같은 테니스장의 매치; 장소 상세 lists the venue's public matches, ongoing or planned, and nothing else of the kind appears elsewhere.

3. Page by page ​

PagePatternsThe fix
L1-01 홈S7the three lanes
L1-03 경기, L1-03c 테니스장S1, S2the strip in the frame's slot; the filter row and the location Notice on the insert law
L1-05 프로필S8the 신뢰 meter
L2-01 클럽 홈S1홈 코트 Cell with the same space above and below; the pane's gaps to the canon
L2-02 일정 상세S1the title 24 under the map band; every gap to the canon
L2-03 매치 미리보기S9no 같은 테니스장의 매치
L3-11, L3-35, L3-36S3an action on each row; no checkboxes, no CTA band
L3-38 스코어카드, L3-39 대진표S6, S1the one match card, live as a Block; the tournament set by the research; the console on the same card; the canon
L4-01 to L4-21S1every gap to the canon
L4-02 클럽 만들기S5the logo generator
L4-03 행사 만들기S4일시 as one Cell; 장소 searches places through Naver

Every other page with a finding in S1's audit is fixed with it. The notes of 10-28 still open on retired cards close with their merges: 테니스장 정보 (L2-05 into L2-04), 공개 프로필 and 신뢰 (L3-02 into L3-01), 게스트 지명 (L3-26 into L3-25), 토너먼트 (L3-40 into L3-38 and L3-39).

4. Library changes ​

  • § 1.6, The rhythm canon: the canon file is the seams' one source; the insert law; how to add a pair.
  • Offline strip: one variant, the frame's slot (the "plain page", "over a MediaBand" and "no header" variants retire).
  • § 1.18: per-person decisions on the row, no bulk CTA (S3).
  • § 1.21, Questions: the recurring questions and their one composition (S4).
  • New kit pieces: the match card's live Block (S6) and the 신뢰 meter (S8).

5. Batches ​

Each batch is one writer per board file, at most three agents, committed green.

  1. The spacing system (S1, S2): the canon, the two checks, the frame's slot, the verifier in the gate, every page to the canon; the review page's save fix.
  2. Actions and questions (S3, S4, S5, S9): rows that act, one when and one where, the logo generator, the venue's matches.
  3. Live, trust and home (S6, S7, S8): the match card and the tournament set, the console on it, the 신뢰 meter, the home lanes.

6. For the owner ​

  1. Your L3-38 note was cut by the review page's save bug (now fixed): it ends "This is getting better, but we". Please add the rest; the page keeps it now.
  2. A group command for money (S3). 참석 확인 gets 모두 확인 on its group head; 참석자 관리 confirms transfers one row at a time, with no command for all, unless you want one.
  3. The live dot (S6). It breathes three times when a match goes live or changes, then holds still, rather than flickering, because constant motion beside other content fails WCAG 2.2.2. Say if you want it to keep moving.

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