Design systemDraft
List
The daily surface: Needs you, Chats, Agents, the vault, devices, settings groups. A list holds rows of one family (needs-row, list-row, settings-row, device-row) under a header and group headers, and keeps the same seven states everywhere. Drawn on the boards "States, every list, every size" (the reference), "Needs you, phone and desktop" and "Devices, network and VyreDrive".
| Surface | Implementing file | Status |
|---|---|---|
| Deck | deck/css/deck.css .rows, .section-head (main, work/pwa) | partial |
| App | apps/app/src/ui/List.tsx (work/mobile) | partial |
| Lumen | local/capsule/native/Sources/UI/CapsuleView.swift results, SectionHeader; UI/AgentDeskView.swift WaitingList (work/capsule-pro) | partial |
Anatomy
- Container. In a pane (desktop list 320 or 360 wide): no box, rows on
--bgwith a 1 px--rulebottom border each. As a boxed list inside a page:--panel, 1 px--rule, radius--radius-card(12),overflow: hidden, rows separated by a--ruletop border (none on the first). Phone: a card per section (radius 10) with the section label outside it. - List header (optional). Height 32, padding 0 16, gap 12, 1 px
--rulebottom: the title (12/600--label) and meta or a sort word right ("Last seen first"). - Group header. Height 32, padding 0 16, gap 8: an optional status mark, the group name
(12/16, 600,
--label), and the count right-aligned (12/16, 400,--label). Needs you's header uses the beacon dot and--beacon-inkfor the name only ("Needs you", then "5 · oldest first"). Phone section label: 17/24 600--text, padding-top 12, the count in 12/16--labelright. - Filter chips (many items): a row of filter chips under the header, each with its count ("All 128", "Asks 71", "Drafts 40"); see the chip spec.
- Load more. A full-width ghost button "Load 40 more" and, beside it, meta
--label"Showing 40 of 128". - Key footer (desktop, keyboard lists): padding 10 16,
--ruletop border, key-hint chips and words in 12/16--label("J K move · A allow · D deny").
Sizes
Rows are 44 minimum (36 in a dense list), phone rows 44 minimum with 17/24 titles. Counts are exact in headers (128); badges cap at "99+".
States
- Empty. Two or three words and the first action right there, inline: "Nothing needs you" and "juno will put anything that needs you here."; "No projects yet" with a name field and Create. Empty never hides the page's other sections, and Needs you empty has no dot and no badge.
- Loading. Skeleton rows at the real row height (tile, title bar, one line, meta). A quiet spinner in the header; at 10 s one line: "The box is slow to answer · Cancel" (Cancel keeps what arrived). The shine stops under reduced motion.
- Offline. Content stays; one pill (see the pill spec). Cached 7 days: projects, agents, the last 20 threads. Never cached: held items and the vault, which say they need the box.
- Error. Plain words, the detail in mono, a way out: "Couldn't load kit's history",
ECONNRESET after 15 s, Retry, Open doctor. The failed mark in--text-2; no red. - Many items. Filter chips, sticky group headers, 40 rows at a time, Load 40 more (never an endless scroll, so J and K stay predictable). Virtualized above 100 rows.
- Long names. Desktop truncates one line with an ellipsis, the full name in the tooltip and the detail header. Phone titles wrap to two lines at larger text sizes; the row grows. Paths truncate in mono and keep the file name.
- Decided. A "Decided today" group with neutral rows (see the needs-row spec).
Keyboard and touch
J and K (and ↓ ↑) move the selection; Enter opens; Home and End jump. Focus stays in the list
while the detail updates. Phone: one inner scroller per page with overscroll-behavior: contain;
the header stays fixed.
Motion
Group headers stick with position: sticky; top: 0 on --bg (on --panel in a boxed list).
Rows arriving live insert without moving the row under the pointer or the selection. A row that
leaves collapses over --motion-tap.
Copy
Group names in sentence case: "Needs you", "Working", "Decided today", "Your devices". "Load 40 more", "Showing 40 of 128". Never "Show earlier", never caps.
Accessibility
role="list" (or listbox where selection means something, with aria-activedescendant).
Group headers are headings (level 2 or 3) so screen readers can jump between them. Counts are
read with the name ("Needs you, 5"). Skeletons are aria-hidden; the header says "Loading".
Gaps
Deck
-
.rowsand.section-headhave no sticky group headers, no filter chips with counts, no key footer; long lists page with "Show earlier" only. - No skeleton rows (lists show "Loading devices." text); no slow-box line at 10 s.
App (work/mobile)
- Virtualized above 100 rows (correct); no sticky group headers, no Load 40 more, no chips.
Lumen (work/capsule-pro)
- SectionHeader is mono 10 caps with tracking; use 12/600 sentence case.
- The waiting list caps at 9 rows (
Theme.maxRows) with no count or more.