The study the owner asked for (2026-08-11): every list row in the app, measured; which differences carry meaning and which are drift; whether one design language can hold them; and the path that gets there without a big-bang rewrite. The persona Studio is excluded — it is being overhauled separately.
.mep-row, .fr-door) turned out to be dead CSS — zero call sites each; the Me landing and the add doors both wear the workhorse already, and the census’s「uses」column had counted their own selectors as callers. The remaining five are the same row drawn five times, and three of today's recent bugs (v761, v779, v781) were paid directly to that
duplication.First the words, marked on one row (fig 1). Then each family drawn with its own real numbers — the same specimen content in every card, so the only thing that differs is the family's own dress. The dashed coral line in every card is the 16px reference inset: where a row's content starts somewhere else, the drift is visible instead of numeric.
.chat-item.drw-item.drw-item, dressed.priv-row.storerow.fr-req.nf-row.drw-item + .drw-ico.mep-row is DEAD CSS: zero call sites (the old Me sheet’s row) — caught by the owner asking 「are these Me rows?」.fr-door dead — v784.acct-field earned| family | lives in | uses | min-h | pad | gap | lead | title | sub-line | hairline |
|---|---|---|---|---|---|---|---|---|---|
.chat-item+.ci-main | chat list · search results · shared chats | 22 | 72 | 10/16·12 | 12 | 46 av | read-font 600 · 15 | 11.5 sans | --line, full |
.drw-item | friends roster · settings · pickers | 48 | 60 | 9/16 | 13 | 40 av / 16 ico | sans 400 · 15 | 11.5 sans (.drw-rs) | --hair-soft, full |
.priv-row | the Privacy & friends toggle rows — reused by Change password · Invite code | 27 | — | 13/16 | 12 | — | sans · 14 (15 in as-page) | 11.5 sans (.ps) | --line, full |
.storerow | archived / deleted / Seen / persona stores | 9 | — | 11/4 (16 in as-page) | 10 | — | serif · 14.5 | mono · 10 | --line, full |
.fr-req | friend requests | 6 | — | 12/16 | 11 | 40 av | sans 400 · 15 | — | --hair-soft, full |
.fr-door | DEAD — zero call sites (the add doors were rebuilt as .drw-item rows long ago); deleted v784 with .dsc-ico | 0 | — | 12/2 | 12 | 26 ico | sans · 14 | — | --line, full |
.mep-row | DEAD — zero call sites (the old Me sheet’s row); the Me landing really uses .drw-item + the coloured 26px .drw-ico tile | 0 | 50 | 0/15 | 11 | 17 ico | sans · 15 | — | --line, border-top |
.nf-row | notifications | 10 | — | 13/16 | 11 | 38 av | sans · 15 | 11.5 | --hair-soft |
.acct-field | account forms (label over box) | 12 | — | 11/16 | — | — | sans muted · 15 | — | --hair-soft |
Read down any column and the drift is plain: gap is 10, 11, 12 or 13 depending on which year the row was written; vertical padding is 9, 11, 12 or 13; the sub-line is 11.5px sans in four families and 10px mono in one; the hairline is soft in three families and full-strength in five — the exact split v693 already outlawed inside one list, still alive between lists; and the lead avatar is 38, 40 or 46px.
| difference | verdict | why |
|---|---|---|
| chat row: 46px avatar, 72px height, timestamp + unread + meta stack | MEANING | it is the app's densest row — three lines of live state, owner-locked avatar spec (docs/chat-list-avatars.html) |
| chat row: read-font + read-scale on title/meta | MEANING | the reading-typography dial applies to content surfaces, and the chat list is one |
| form row: label ABOVE a full-width box (password, invite) | MEANING | v764–768 settled it: a field you type into wants the row's width; a control you glance at sits beside its label |
| gap 10 vs 11 vs 12 vs 13 | DRIFT | no rule decides; each author picked |
| pad-y 9 vs 11 vs 12 vs 13 | DRIFT | same |
| store title in serif · store meta in 10px mono | DRIFT | the store predates the drawer language; v761 already had to re-dress its headings once |
| hairline --line vs --hair-soft | DRIFT | v693 ruled ONE hairline weight; the rule was applied per-list, never app-wide |
| lead avatar 38 vs 40 | DRIFT | nf-row's 38 is 2px off the roster's 40 for no reason anyone can name |
| priv-row 14px face vs everyone's 15 | DRIFT | already overridden to 15 wherever it shares a screen with drawer rows (v761) |
The owner's decision (2026-08-11): unify everything — only the chat avatar stays 46. So the unified numbers are: inset 16 · gap 12 · title 15 · sub-line 11.5 sans · hairline soft · lead 40 avatar / 26 icon-tile (chat avatar 46) · min-height 60 nav / 72 chat. Kept on purpose: the chat title's semi-bold + reading-font dial (both owner calls), and the field row's label-over-box shape. Below, every family that changes — BEFORE wearing today's numbers, AFTER wearing the unified ones, the same dashed 16px guide through both.
.storerow — the biggest move.fr-door MOOT — the doors were already drawer items; rules deleted v784.chat-item — avatar stays 46.priv-row.nf-row.drw-item — the reference, barely moves.drw-item.drw-item.drw-item.row class is NOT recommended: 150 call
sites, and this file's history says exactly what that buys.--row-inset:16px · --row-gap:12px · --row-pad-y:11px · --row-title:15px ·
--row-sub:11.5px · --row-hair:var(--hair-soft) · --row-lead:40px ·
--row-lead-ico:26px · --row-lead-chat:46px and re-express all nine families in them. Where a family's
current number differs from the token (gap 10/11/13, pad 9/13, sub 10 mono, hairline
--line, lead 38), it adopts the token — these are the drift cells in §1, each a
≤3px move or a colour-weight change. The chat row keeps its two earned overrides as
its own tokens. And the dead .mep-row rules are deleted outright — a dress with no wearer..fr-req and .nf-row are .drw-item with a
trailing action / unread dot → variants. .storerow is
.drw-item with two trailing buttons → variant (its serif title and mono
meta do not survive; v761–782 already moved it halfway). .priv-row stays
as the control-row variant (label left, control right — including its
stacked-box form from v768). .mep-row and .fr-door retire
into .drw-item. Target: 9 families → 3
(.drw-item + variants · .chat-item · the field row).Three of today's bugs were interest paid on this duplication:
.fr-kick,
.store-grp and .acct-field label to look like
.drw-gtitle: three copies of one heading rule, now four places to drift.#chatsList's padding and broke on #frList's: two hosts,
two paddings, one hard-coded assumption..priv-row
powers three live pages; its variant must keep the v768 stacked-box form. ③ The chat
row's avatar spec is owner-locked — Stage 1 touches its paddings/hairline only via
tokens with today's values, moving nothing. ④ Search results draw chat rows inside
foreign hosts (v779); the merge order keeps .chat-item untouched until the
hosts share one inset token, which Stage 1 itself delivers.The owner’s question, answered: yes — after this, a new or reworked page is ordered by NAME, not by numbers. One sentence like「make these drawer items, bare icons, button trail」is a complete spec. This section is the menu that sentence draws from, and the study is the contract that the name always produces the same row.
| you say | what gets built | the choices left to make |
|---|---|---|
| 「drawer items」 the default for any list |
.drw-item — the workhorse, at the tokens |
lead: 40 avatar · 26 coloured tile · bare icon · none trail: › · count badge · action button · none sub-line: yes / no |
| 「control rows」 「like Privacy」 |
the control variant — label left, control right | control: toggle · shown value · a box on its own line (the v768 form) |
| 「chat rows」 | roomRowEl(r) — a BUILDER, not markup: time, unread, meta and
long-press menu come with it |
none — it is the one row ordered as a component, which is why search results and Shared chats already look right for free |
| 「field rows」 | .acct-field — label over a full-width box |
grouping: which fields share a card-gap (one question vs one act) |
| 「store rows」 | drawer items with a two-button trail (Stage 2, shipped) — not a family, just an order | the verbs on the trail |
And the same convention covers editing stale pages: 「this page’s rows should be drawer items」is the entire migration instruction — exactly the sentence that turned the store pages around in v761–782, except that after Stage 2 it is one class swap instead of a re-dressing.