4. UX quality bar (every screen, app-wide; implementation must pass every applicable item)
This applies to every routed screen, whether it is specified here or in ux-screens-longtail.md,
across staff, member, public, auth and kiosk. Run it per screen at review time.
Composition
- The screen is exactly one shell plus one layout from §1A (L1–L10), with slots filled. There is no bespoke page-level grid, header or empty/error handling outside the layout. Any deviation is listed in the PR with a reason.
- The layout receives already-gated tabs, nav and actions. The screen adds no gating branches to the layout.
Structure
- Exactly one filled primary per view region (page header, drawer footer, sticky card, callout). It never targets the current screen.
- A title plus either a greeting/date eyebrow (landing pages) or a one-sentence scope subtitle (inner pages). Detail pages: back link, identity tile, title, status pill, meta line.
- Detail views use underline tabs that are URL-addressable. Tabs whose content is gated are absent, not disabled.
- Counts live in stat cards, tab labels, row right slots or header meta. The sidebar carries only Inbox unread and the Requests needs-action count.
- A callout appears only when actionable now. At most one per screen. It has a bold lead clause, a consequence and an action.
- Destructive-irreversible actions sit in a danger card at the end, with a “…” button that opens a confirmation naming the object and the consequence.
Lists
- Every list has an empty state that names the situation and the next step, pointing at a visible control. Filtered-empty differs from truly-empty and offers “Clear filters”.
- Rows have one inline next-step action; everything else is in the row menu.
- Loading = skeletons in final geometry; error = inline block plus Retry; pagination states “1–24 of 248”.
- Status is never colour-only: every pill has a word, and overdue chips carry “Overdue n days”.
Feedback and safety
- Reversible actions run immediately with Undo (inline where the row stays, toast where it
leaves the view), using the existing
toastStoreawaited compensation. Undo is never offered for evidence, handovers, audit, payments or settlements. - Hard-rule steps show why the primary is disabled (“Add the return photo and a remark to continue.”).
- Saves: an explicit Discard/Save that appears when dirty for field groups; instant toggles confirm with a toast. Never two save models in one section.
- Every mutation has a pending label (“Saving…”) and disables only its own control.
Copy
- Sentence case; consequence-first helpers; numbers in context (“12 / 16 going”, “Due Fri”);
relative times with the absolute time in
title/tooltip; “…” on buttons that confirm; “↗” on external links. - Error copy says what failed and what to do (“Couldn’t save. Try again.”), never raw codes.
Access and devices
- Keyboard: the full flow is reachable by Tab; tabs use roving focus with arrows and Home/End; Esc closes drawers and modals and returns focus to the opener; Ctrl/⌘K opens the palette from anywhere.
- Focus ring ≥3:1; 44 px minimum targets; one
h1; the skip link targets#main-content(tabIndex=-1); collapsed content unmounted; reduced motion respected (no auto-advance animation, a quick fade only). - Mobile (375): no horizontal page scroll; header collapses to primary plus ⋯; tables become stacked cards; sticky primary at the bottom where the task is the page (handover, request cart, register); drawers go full screen.
- Camera views start only on explicit intent, except task-mode scan steps where scanning is the step. There is always a manual-entry fallback under the camera.
- Test pins (PLAN §2) are preserved or deliberately updated in the same commit.
Part of Space Works UX spec, published from docs/ux-spec.md. Edit it there.