Patterns & System States
The cross-cutting states and in-context failure patterns every later phase links into. Built once, here, so a broken link found in Phase 5 or a permission boundary hit in Phase 10 lands somewhere considered rather than somewhere improvised.
Which state, when
Six of these look superficially similar and get confused constantly. The distinguishing question is always whose fault, and what still works. Pick from this table rather than from instinct.
| State | Use when | Blocks | Tone |
|---|---|---|---|
| Splash | The shell, session or configuration is still loading — cold start, or warm start after sign-in. | Everything, briefly | Processing |
| 404 | The route is invalid. Nothing exists at that address — a stale bookmark, a mistyped cheque ID. | The page only | Neutral |
| 403 | The resource does exist; the user's role doesn't reach it. Never dress this up as a 404. | The page only | Restricted |
| Maintenance | Planned, server-side, platform-wide. Pre-empts even Login. | Everything | Planned |
| Offline | Client-side network loss mid-session. The user still holds local context worth protecting. | Nothing — banner only | Degraded |
| Crash boundary | An unhandled client-side error. Unexpected and ours, not the user's. | The content area | Unexpected |
| Request timeout | One action didn't come back in time. The rest of the screen is fine. | The action only | Retryable |
| Widget failure | One panel on an otherwise-working screen couldn't load its data. | That panel only | Contained |
Offline — persistent banner, never a takeover
A reviewer who loses connectivity mid-cheque may be holding a half-written rejection rationale. A full-screen offline state would throw that away to tell them something a strip of colour can say. The banner slides in under the topbar and stays until the condition clears.
| State | Banner | Behaviour |
|---|---|---|
| online | None | Heartbeat animates. Nothing is said, because nothing is wrong. |
| offline | Amber | Persistent. Names what happens to work in progress. Offers Retry now. Heartbeat flattens. |
| reconnected | Green | Transitional — "Back online — syncing…" — then clears itself and confirms with a toast. |
Widget failed to load
Contained to the panel that failed, with its own retry. Previewed here because Phase 3's Dashboard is where it earns its keep — one dead chart must never cost a reviewer the whole screen.
Exceptions by reason
Formula: contained mark · one-line headline · one sentence that says what is still trustworthy · a Retry scoped to this panel · a reference ID for the service desk. Never a stack trace, never a full-page escalation.
No data yet — one formula, three situations
Empty is not an error, and it should never look like one. The same four-part formula covers a filtered search with no hits, a queue that is genuinely clear, and a screen a user has reached for the first time.
Filtered — no hits
No cheques match these filters
Nothing for 14 Aug 2026 in this decision state. Widening the date usually helps.
Genuinely clear
Nothing awaiting your approval
Every exception assigned to you has been actioned. New items appear the moment a maker submits.
First-time use
No cheques uploaded yet
Upload a cheque image or connect a scanning-center batch feed to get started.
| Part | Rule |
|---|---|
| Mark | One icon in a neutral tile. Tint it only when the emptiness is good news (valid green) or an invitation (brand). Never danger red — empty is not broken. |
| Headline | States the fact in the user's words. "No cheques match these filters", not "No results found". |
| One sentence | Says why it's empty and what would change it. Two sentences maximum. |
| Action | Present only when there is a real next step. A genuinely-clear queue gets no button — inventing one manufactures work. |
Request timeout — inline, beside the action
Distinct from offline: the connection is fine, this one request didn't come back. It belongs next to the button the user pressed, so they can see what failed and press it again — not in a toast that slides away, and never as a full-page state.
Live demo · first submit times out, second succeeds
| Attempt | Treatment |
|---|---|
| Timed out | Amber inline block above the action. Says the request may still have gone through, and that retrying is safe because submission is idempotent. |
| Failed outright | Same block, red. Names the reference and offers the service desk. The button stays enabled. |
| Succeeded on retry | Inline block clears, success toast confirms. No trace of the failure is left on screen. |
Destructive confirmation — one treatment, one button order
Every irreversible or sensitive action in every later phase uses FQ.confirm(). Fixing the treatment now is what stops eleven phases inventing eleven confirmation dialogs with the buttons in different places.
Live examples
Each opens the same dialog with the same geometry. Only the words change.
| Rule | Why |
|---|---|
| Cancel is left, ghost | Lowest visual weight, first in reading order. Leaving is always the easy move. |
| Destructive is right, solid danger | The destructive action is never allowed to be the softer-looking button. Hiding consequence behind a quiet button is how mistakes get made at 4pm on a Thursday. |
| Danger seal, never a red page | One red mark in the dialog head carries the weight. No flashing, no shaking — the platform stays calm about its own gravity. |
| Reason is mandatory | Irreversible and dual-controlled actions require a typed rationale before the confirm button will act. |
| Escape and the scrim cancel | Only the explicit right-hand button commits. Nothing destructive ever happens by dismissal. |