—On a wide window there are two: the list on the left (363), the page on the right. On a phone the right pane IS the screen.
363
363
Every object this app is built from, rendered once, with the name we call it. Order a block by name — “a contact row with a checkbox”, “an entry row with Restore and Purge” — and that is a complete instruction; nobody has to remember a class.
The stylesheet on this page is the app's own, copied verbatim, so a specimen here cannot
drift from what ships. Rebuild with python scripts/build_catalog.py after any UI
change. Two rules run underneath everything below: every block is standard unless it is
named special, and a right-pane page is 600 wide unless it says otherwise out loud.
Every name on this page, and the class behind it.
| Name | Class | Section |
|---|---|---|
| the pane | — | Geometry |
| the column | 600px, centred | Geometry |
| chrome | — | Geometry |
| sub-page header | .drw-head | The page |
| group | .drw-group | The page |
| group title | .drw-gtitle | The page |
| page-wide button | .drw-wide | The page |
| the foot | .modal-foot | The page |
| chat row | .chat-item | the chats list · Forward · Shared chats |
| contact row | .drw-item.ppl-row | The rows |
| drawer row | .drw-item + .drw-chev | The rows |
| toggle row | .drw-item.priv-row + .priv-sw | The rows |
| dropdown row | .drw-item + .dd | The rows |
| text row | .drw-item.priv-row + .priv-box | Change password · Invite code |
| entry row | .drw-item.storerow | Archived chats · Deleted chats · Deleted Seen · Deleted persona |
| search row | .srch-field / .modal-search | Invite people · Invite persona · the search page · the persona browser |
| pill row | .pb-sort | Invite persona · the persona browser |
| a frozen row group | .pick-tools / .pick-row | Invite persona (search + pills) · Invite people (search only) |
| commit button | .drw-commit / .dialog-commit | Buttons |
| primary / ghost | .btn-primary / .btn-ghost | Buttons |
| small button | .np-prev | Buttons |
| pill | .lib-mine | Buttons |
| icon button | .iconbtn | the chat top bar: the wildcard, and back-to-chats on a phone |
| the head pair | .head-ico | the head of the Chats · Friends · Discover lists |
| bubble | .msg / .bubble | In the chat |
| avatar | .fr-av / .avatar | In the chat |
| toast | .toast / .toast.show | any act with an undo — archive, delete, mute |
| badge | .drw-cnt | Feedback |
| empty state | .store-empty | Feedback |
Where a thing sits, before it is anything else. These are diagrams: a fixed full-screen page cannot be rendered inside a box.
—On a wide window there are two: the list on the left (363), the page on the right. On a phone the right pane IS the screen.
600px, centredAnything you read or fill in is 600 wide and centred. THE DEFAULT — a page opts out, never in. ⚠ Cap the CONTENT, never the scroller: the scroller stays the pane so its bar sits at the far right. Special by name: the chat (790) and the Vibes feed.
—A header, a footer, a boundary line: chrome spans the PANE, so it is the one thing that is not capped. A hairline under a header is the line between what freezes and what scrolls.
A sub-page is a header, a body that scrolls, and sometimes a foot.
.drw-headBack ‹ · title · commit. The ‹ sits 2 from the pane's edge, the commit 10 from the other. One tap on ‹ leaves, from any depth.
.drw-groupThe band a run of rows sits on: a surface with a soft edge top and bottom. Rows inside are parted by ONE hairline. The break below a group is 10.
.drw-gtitleSits on the PAGE, not on the band, and lines up with the rows' icon column. A titled group drops its top edge — the title is already the separation.
.drw-wideA control on the PAGE, not a row of the list — so it keeps the page's own side margin rather than running edge to edge. Sits 22 below what it follows. Log out is the one everyone knows.
.modal-footPinned under the scroller; holds what you have picked. Even air, 10 and 10, plus the home-indicator inset below that.
The owner's own menu — NINE names since v820, when the search field and the pill row were named rows in their own right. Most of them are ONE class, .drw-item, the workhorse, wearing a different lead and a different trail. Order a row by NAME and it is a complete instruction: “a contact row with a checkbox” needs no drawing.
.chat-itemA chat in a list. The room's TILE leads it; title · time on the first line, who is in it on the second, in their seat colours. Always order the component (roomRowEl) — never re-draw it. ⚠ It bleeds ±16 by design (v711), cancelling the list's own inset, so its stage has to give that back.
lives in the chats list · Forward · Shared chats
.drw-item.ppl-rowA person. 40px round avatar as the lead, the name at 15. One line — the handle is not printed underneath.
.drw-item + .drw-chevA door: press it and you go somewhere. Icon lead, label, a chevron in the trail. The sub-line under the label says what it is for.
.drw-item.priv-row + .priv-swA setting you flip in place. The switch is the trail; nothing navigates.
.drw-item + .ddA setting you pick from a list. Same workhorse; the trail is a dropdown showing the current value.
.drw-item.priv-row + .priv-boxA label over a box you type in. ⚠ It IS the workhorse — since v785 the password and invite fields ride .drw-item.priv-row, which on a sub-page turns its column form on. So it keeps the row's own inset and height, and its label is the row title (house sans 15), not a caption. .acct-field — 11px mono label, no row padding — is the OLD field family and survives only inside Edit profile; showing that here was wrong twice over (owner-caught).
lives in Change password · Invite code
.drw-item.storerowA stored thing with its own buttons in the trail. Restore · Purge — the store dress.
lives in Archived chats · Deleted chats · Deleted Seen · Deleted persona
.srch-field / .modal-searchA ROW whose whole content is the field — 36px round pill, magnifier lead, a clear × once you type. It takes the --bg fill when it sits on a group, because a surface field on a surface band is invisible. Its hint text is the shared muted tone, stepped back.
lives in Invite people · Invite persona · the search page · the persona browser
.pb-sortA ROW whose content is a run of pills — a sort or a filter you pick one of. Same inset, same hairline, same wash as any other row; the pills are the house pill, never a copy of its spec.
lives in Invite persona · the persona browser
NOT an object — a PROPERTY you apply to a row or to a row group. The frozen part stays put; everything below it scrolls. Because the frozen part is just rows, it needs no special dress and no hand-drawn line: the last row's own edge IS the boundary.
.pick-tools / .pick-rowA search row and a pill row in one group, frozen — the shape the Invite persona page uses. The rows beneath scroll under it. On Invite people the same thing is done with a single frozen search row.
lives in Invite persona (search + pills) · Invite people (search only)
Four jobs, four dresses. A bare text link is not one of them — this app has none.
.drw-commit / .dialog-commitThe one act a page exists for, in the header's right corner. Coral when live, grey when there is nothing to commit.
.btn-primary / .btn-ghostA dialog's pair: the act, and the way out of it.
.np-prevA control inside the content, not the chrome: 1px line on surface, radius 8, 13px muted, 36 tall. The ← is in-content; a ‹ chevron is always chrome.
.lib-mineA filter or a sort. One shared spec — never copied, or it is consistent right up until the spec moves.
.iconbtnA bordered glyph button in the chat's top bar. ⚠ NOT retired, though it looks it — audited live because the owner asked: on a phone TWO of them paint (back-to-chats 31×29, the wildcard 39×35); on a wide window just the wildcard, 39×35. Two more exist and are normally invisible — the admin's back-to-console (peek mode only) and the model tag (hidden by a role flag). The magnifier and the ⊕ the owner had in mind are different objects.
lives in the chat top bar: the wildcard, and back-to-chats on a phone
.head-icoThe magnifier and the ⊕ — ONE button each, the same two glyphs on Chats, Friends and Discover, so they can be aimed at without reading; only the action behind them changes with the root (v704, WeChat's header). A different object from the icon button above: no border, bigger glyph, and it lives in the LIST's head rather than the chat's top bar.
lives in the head of the Chats · Friends · Discover lists
The room's own objects.
.msg / .bubbleA persona speaks in its SEAT colour — a per-room colour, not a per-persona one. The human's own bubble is the coral one.
.fr-av / .avatarOne scale everywhere: 24 inline · 30 row · 40 card · 64 profile · 84 hero. A photo when there is one, the monogram when there is not.
What the app says back.
.toast / .toast.showA warm taupe pill, never a black slab. It says what happened and, when there is one, offers the undo. ⚠ It is visibility:hidden until it carries .show — a specimen without that class renders nothing at all, whatever else you override (which is exactly what happened here until the owner said so).
lives in any act with an undo — archive, delete, mute
.drw-cntA count in a row's trail.
.store-emptyA sentence where the rows would be — never an empty card behind it.