RelayCTX Creative guidelinesGUIDE.list-surfaces.md
RelayCTX Creative · Document

GUIDE — List surfaces & card anatomy

1,421 words · 6 min View raw Source History

GUIDE — List surfaces & card anatomy

Status: law — v1, 2026-08-03 Applies to: every list in the product — Transfers, Series, Sessions, Stream, Search, Requests, Connections, and the console equivalents Parent: GUIDE.interface-principles.md Build spec: the shared list shell is specified in relay-app/docs/DESIGN-NOTE.list-views-refresh.md (epic #449). This document is the design law that shell implements.

Lists are where people spend their time. They are also where the governing principle gets violated most often, because a list row is where designers are most tempted to show everything.


Card anatomy

A row answers three questions, in this order, and nothing else at rest:

Governed by DEC-025. The card head is already law — see ../briefs/BRIEF.code-presence.md (display laws L1–L4) and relay-app/docs/PLAN.code-presence-experience.md. This section states how those laws compose at phone width; where they differ, DEC-025 wins.

Shared (has a Code)                    Unshared (no Code — head closes up)
┌───────────────────────────────┐      ┌───────────────────────────────┐
│ ●  ABC123                     │      │ ●  Title, fully legible       │
│    Title, fully legible       │      │    7h ago                     │
│    7h ago · EXPIRED           │      │    tag  tag  +3        [Share]│
│    tag  tag  +3               │      └───────────────────────────────┘
└───────────────────────────────┘
SlotCarriesRules
Code (head)The Code when the object has oneMono, letter-spaced, --accent-textthe brightest element in the card (L2). When absent the slot closes up; the layout never leaves a hole and nothing is promoted into it (L3).
TitleThe object's labelSame slot, same size, same weight, same colour in both states (L3). Its job is recall, not identity. Fully legible at two lines — never truncated to a stub.
Status dotUnread / needs-attentionOne dot, one meaning. Not a second badge system.
Meta lineRelative ticking time · exceptional state onlyOne line. Honest time — exact on press.
TagsUp to 2 + +nInline, never wrapping to a third line.
TrailingOn unshared objects, the share action--accent-fill + --on-accent, the one teal moment for that state (L2). Otherwise one affordance, or none.Everything else lives in the row's sheet or the detail view.

The two failure modes, named:

  1. Truncating the title to a stub. Firm… / launc… is not a title — two rows rendered that way are indistinguishable. This is a legibility failure, and it is independent of rank: the title keeps its rank, and it must still be readable. DEC-025's title floor (§12 Q13) says titles must be reliably present before any surface leans on them.
  2. Reaching for the title when the Code is absent. L3 exists precisely because "a designer sees empty space where the Code was and reaches for the nearest text field." Title-as-hero on codeless objects was proposed and rejected — the title is the most generic field in the schema and cannot carry a verb surface. The head closes up instead.

The badge rule

Render the exception, not the default.

ACTIVE · PRIVATE · PERSONAL on every card is not information — it's the norm rendered as data, and it costs the title its space (EXP-005). Show a state badge only when the state deviates from what the user should assume:

ShowDon't show
EXPIRED, REVOKED, SPENTACTIVE
ORG, BROADCAST when scope is unusualPRIVATE, PERSONAL
DUE, EXPIRES <n> when neara distant expiry

If a badge appears on more than about three quarters of rows, it is chrome. Move it to the detail view or delete it.

Absence is addressable, not annotated (DEC-025 L4). The strongest form of this rule: don't stamp "never shared" — or its cousins — on every row. Make the set selectable as a filter, so absence reads as a population — "41 of 68 never left this workspace" — instead of a mark repeated 41 times that each individually looks like missing data. When you're tempted to add a badge to express a state that's true of many rows, add a filter instead.

No title is a content problem, not a layout one

An untitled object must never fall back to a raw internal ID. 8D9F34E2E7CA tells the user nothing and isn't even the Code they'd share.

The real fix is upstream — DEC-025's title floor (§12 Q13): titles must be reliably present before any surface leans on them. Derive one at creation rather than papering over the gap at render time. Where a row must still cope, fall back in this order:

  1. The context note / synopsis
  2. The first meaningful line of content
  3. A minimal honest label — last resort only, and never N/A, an empty string, or an internal ID

A placeholder is a symptom. If rows are hitting step 3 with any regularity, that's a finding against the title floor, not a card-design problem.


The list shell

Every list uses the same anatomy. Divergence needs a written reason.

Above the list — at phone width, two rows maximum (see GUIDE.mobile-composition.md):

The list itself:


Filtering

Filters are combinable and stateful, never a single-select tab strip.

The failure to avoid: a tab row mixing unrelated axes — direction (Sent/Received), status (Pending/Delivered), and personal state (Unread/Due/Starred) on one strip, single-select, so "Received + Unread + tag:mobile" is unexpressible.

Rules:


Density and scale

Design every list against three data volumes, and check all three:

VolumeWhat it tests
0The empty state — is there a first action?
3The demo case — does it look composed, not sparse?
500+The real case — does it scroll, filter, and find?

A list that only looks right at three rows is a mockup, not a design. Most recorded friction shows up only at the third volume.


Selection and bulk action

Multi-select exists only where handling many objects at once is a real behaviour. Where it does:


Checklist


Framework note

Card anatomy, the badge rule, the filter model, and the three-volume check are generic to any object list — framework-candidate for Launchpad Phase 2. Relay-specific: the Code slot and the claim/handoff state vocabulary.