# UI phases — giving Balinyaar the interface its product deserves

**Created:** 2026-07-16 · **Scope:** `client/` only (frontend track) ·
**Method:** a 15-agent parallel design audit over every UI surface of the client (theme/tokens, shells,
component primitives, feature widgets, auth, storefront, booking, checkout/money, account, nurse workspace,
nurse trust/ops, admin/partner, messaging/notifications, cross-cutting UX patterns, fa/en microcopy).
The full per-area findings — with file/line evidence — live in [`audit/`](audit/).

This directory is a **runnable chain of 14 UI phases** (0–13) that takes the client from *"feature-complete
but visually a default-MUI starter"* to *"a designed, branded, Persian-native product."* The functionality is
already there — this chain is about **look, feel, hierarchy, trust presentation, mobile/RTL/Persian
nativeness, and the UX defects the audit surfaced along the way.** Run the phases **in order, one at a time**,
pointing a fresh agent at one phase file (*"Execute `dev/post-phase/ui/ui-phase-0-design-language.md` end to
end"*). After phases 0–2, most later phases touch disjoint route trees and can be parallelized if you accept
some risk; the recommended path is sequential.

> **Relation to the other chains.** The [refinement chain](../refinement/README.md) made the app *work* as one
> integrated system (it is complete). This chain makes it *feel* like a product. It deliberately does **not**
> touch `server/` — where a UI improvement needs a backend change, the phase files a REQ in
> [`for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md) and builds mock-tolerant
> UI behind the existing `services/{domain}` seams (REQ-001…038 are taken; number onward from there).

---

## What is already good (do not regress it)

The audit found the feature layer unusually disciplined. Every phase must preserve:

1. **Token discipline** — 331 `var(--bal-*)` usages across 103 files, effectively **zero hard-coded hexes**
   in feature code. The two-layer token system (`tokens.css` ↔ `colors.ts`) works; extend it, never bypass it.
2. **The four-state data pattern** — skeleton → error-with-retry → empty → data is genuinely implemented on
   most feature pages (search results, earnings, verification, tickets…). The gaps are enumerated per phase.
3. **RTL as a habit** — logical properties (`borderInlineStart`, `textAlign: 'start'`, `marginInline*`),
   deliberate `dir="ltr"` islands for phone numbers/IBANs/clocks, RTL-mirrored message bubbles.
4. **Persian correctness plumbing** — `utils/money.ts` (BigInt IRR, Toman-at-the-boundary, fa digit
   grouping), `utils/date.ts` (Shamsi via `Intl` `fa-IR-u-ca-persian`), locale digits in counts and pagers.
5. **Product-honesty components** — `EscrowNotice` (product-mandated verbatim copy), `RefundEtaBanner`'s
   honest BNPL 7–10-day window, `TrustBadge`'s three honest states, EVV's advisory-never-blocking semantics,
   the two-stage clinical/address disclosure gates, negative-balance "owed back" rendering.
6. **The architecture seams design work needs** — the `AppIcon` string registry (one-file icon swap), the
   per-actor shell split (`CustomerLayout`/`NurseLayout`/`AdminLayout`/`PartnerLayout`), component defaults in
   `components/config.ts`, the `services/{domain}` mock seams, the admin composite layer
   (`AdminDataTable`/`ConfirmDialog`/…), optimistic ticket send with `clientMessageId` reconciliation.
7. **Copy that is already right** — the escrow/payout/refund explainers, the culturally-tuned gender copy,
   the formal-شما register, specific actionable verification-failure reasons.

---

## What is actually wrong (the problem inventory)

Every item is verified in code; file/line evidence is in [`audit/`](audit/) and repeated in the phase files.

### A. The starter is still the face of the app
- `createTheme` has **zero `components` overrides** (`theme/theme.ts`) — every Button/Card/TextField/AppBar/
  Chip/Dialog/Stepper renders stock Material with recolored primaries. This alone is most of the
  "old MUI beginner example" feel.
- The **brand logo is the starter's Twemoji cartoon pencil** (`AppIcon/icons/PencilIcon.tsx`), rendered at
  56px on the auth screens and in every top bar. The seed-deck mark (deep-teal square, cream glyph,
  terracotta dot) was never built.
- The nurse/admin/partner chrome is the untouched starter shell: solid-primary fixed `TopBar` with a static
  centered label, flat 10-item sidebar whose **active highlight never fires** (locale-prefix bug in
  `SideBarNavItem`), a permanent `UserInfo` placeholder ("Current User" / "Loading..." in English), physical
  `paddingLeft/Right` RTL hazards, and a desktop SSR mobile-first flash.
- The login page — the only front door — is wrapped in starter dashboard chrome titled **"Unauthorized -
  Balinyaar"** in English; `ErrorBoundary` is unstyled English with a raw stack dump; `globals.css` is the
  starter reset; `light.ts`/`dark.ts` are dead starter themes still exported.

### B. The design system is missing a layer
- MUI palette has **no success/error/warning/info**, so inline alerts show stock MUI green/red while toasts
  show brand `--bal-*` colors — two feedback systems for one semantic state.
- **Persian typography is Roboto metrics**: non-zero letter-spacing on a joined script, tight heading
  line-heights that clip Persian ascenders, requested weight 600 that Mikhak doesn't load (renders 700), an
  EN brand font declared but never wired.
- Tokens cover **color only** — no elevation/shadow (grey MUI shadows on warm cream), radius scale, motion,
  or focus-ring tokens; exactly **one** `:focus-visible` style exists in the whole app.
- The icon registry mixes filled/outlined generations, has **no back/chevron icon at all**, and `AppIcon`'s
  `size` prop is silently broken for MUI icons. `AppButton` ships a starter `margin: 1` default that 213
  call-sites in 82 files neutralize with `sx={{ m: 0 }}`.
- Missing primitives pages keep hand-rolling: EmptyState/ErrorState (29 dashed-Paper copies in 23 files),
  PageHeader, Money display, card anatomy (~12 hand-rolled Paper recipes), skeleton twins, relative time,
  a `formatNumber` helper (the `fa-IR` ternary is copy-pasted 25+ times) — and **no Jalali date picker**,
  so every date input in a Shamsi-displaying product is a native Gregorian `type="date"`.
- No route-level `loading.tsx`/`error.tsx`/`not-found.tsx` anywhere; one static `<title>` for ~60 routes.

### C. Trust surfaces undersell the product
Trust *is* the product, and the UI treats it generically: a bare login card with no trust presence; a static
✓ chip that never explains **what** was verified; a checkout whose total is a `subtitle2` row; a payment
confirmation with **no reference code**; a nurse verification journey with two competing progress metaphors;
a nurse public profile that doesn't answer "would I let this person into my mother's home?".

### D. Real UX defects found along the way (fixed by their area's phase)
The audit found genuine bugs beyond styling — the worst: a cancel-request dialog whose **dismiss button is
labeled with the destructive action**; the error→false-empty pattern (a failed query renders "you have no
patients/services/requests"); **no route to a customer's pending requests** (`useCustomerRequests` is wired
to nothing); **no sign-out anywhere in the customer shell**; `PublishGate`'s **fake success snackbar**;
bookings list unpaginated (booking #21 unreachable); the nurse day-of flow has **no address, no contact, no
navigation affordance**; `DocumentUpload`'s rejected-state re-upload shows no progress; partner booking
statuses render as raw English `snake_case`; admin actions target users by hand-typed numeric ID.

### E. Mobile-native and Persian-native gaps
No safe-area handling under the customer/nurse bottom navs; OTP inputs without `autocomplete="one-time-code"`
/ WebOTP in an OTP-first market; Gregorian date pickers everywhere; Latin digits in timers; fa catalog missing
ICU plural/zero forms («مشاهده ۰ پرستار»); desktop rendered as an 800px phone column with a mobile tab bar.

### F. Copy defects on trust-critical strings
The brand name is spelled two ways («بالین یار» vs «بالین‌یار»); تأیید appears with and without hamza (63
occurrences, both forms); grammar bugs sit on nurse-facing EVV errors and customer address hints; BNPL copy
uses banker's jargon (نکول) and one sentence states the **inverse** of the intended risk allocation;
policy numbers (72h dispute window, cancellation tiers) are hard-coded into copy the admin config can change.

---

## The 14 UI phases

Phases 0–2 build the foundation everything else composes from. Phases 3–11 are area redesigns over disjoint
route trees. Phase 12 is the closing sweep; phase 13 is optional and product-gated.

| # | Phase | Delivers | Depends on |
| --- | --- | --- | --- |
| **0** | [Design language & theme foundation](ui-phase-0-design-language.md) | brand mark, `theme.components` pass, semantic palette, Persian type scale, one icon family, token extension (elevation/motion/focus/rating/trust/money), starter purge | — |
| **1** | [Shared primitives & app-wide states](ui-phase-1-primitives-and-states.md) | EmptyState/ErrorState kit (+ error→false-empty fixes), PageHeader, card kit, `<Money>`, Jalali date picker, StatusChip v2, StatusTimeline, CountdownTimer v2, RatingInput v2, skeleton twins, route-level loading/error/404, per-route metadata, formatting utils | 0 |
| **2** | [Shells & navigation](ui-phase-2-shells-and-navigation.md) | locale-aware nav (`createNavigation`), contextual customer header + safe-area bottom bar, grouped nurse sidebar + nurse bottom nav + identity card, dense admin chrome, de-startered public shell, sign-out / role / locale switchers | 0, 1 |
| **3** | [Auth & first-run](ui-phase-3-auth-and-first-run.md) | trust-forward login hero, OTP autofill (WebOTP), terms/privacy consent + pages, illustrated select-role, focused onboarding wizard, returnUrl | 0–2 |
| **4** | [Customer storefront](ui-phase-4-customer-storefront.md) | home that sells, Shamsi date filter, sticky search CTA, honest results header + NurseResultCard v2, nurse profile as trust dossier + verification explainer | 0–2 |
| **5** | [Booking lifecycle](ui-phase-5-booking-lifecycle.md) | C4 trust-anchored request form, C5 countdown ring + recovery, customer requests tabs on /bookings, booking-detail hero + vertical timeline, cancel-flow fixes, review context | 0–2 (4 recommended) |
| **6** | [Checkout & money](ui-phase-6-checkout-and-money.md) | checkout hierarchy + sticky pay bar, receipt-grade confirmation, wallet as money hub, honest BNPL comparison, fiscal-grade invoice, designed wait states | 0–2 (5 recommended) |
| **7** | [Nurse daily ops](ui-phase-7-nurse-daily-ops.md) | the real nurse dashboard, visit workspace (address/contact/EVV-first), decision-first request inbox + urgency system, earnings clarity | 0–2 |
| **8** | [Nurse business & verification](ui-phase-8-nurse-business-and-verification.md) | activation checklist (honest PublishGate), unified verification journey, DocumentUpload fixes, variant-builder preview, coverage viz, bank flows, public-profile preview | 0–2 (4 for trust components) |
| **9** | [Customer account & care circle](ui-phase-9-customer-account-and-care-circle.md) | profile as account hub (+ sign-out entry), care-circle reframe with avatars, per-item care-record editing, real map picker, mobile full-screen forms | 0–2 |
| **10** | [Messaging & notifications](ui-phase-10-messaging-and-notifications.md) | chat-grade ticket threads (live, scrolled, grouped), inbox pagination/filters, right-sized emergency affordance, notification center grouping + bell popover | 0–2 |
| **11** | [Admin & partner console](ui-phase-11-admin-and-partner-console.md) | URL-synced list state, user/nurse pickers (no raw IDs), verification desk, ticket console lifecycle, AdminDataTable v2, Jalali inputs, partner localization | 0–2 |
| **12** | [Copy, motion & final polish](ui-phase-12-copy-motion-and-polish.md) | Persian style guide + catalog sweep, ICU plurals, trust-moments copy, config-served policy numbers (REQ), motion pass, a11y sweep, 4-axes QA | all prior |
| **13** | [Public front door](ui-phase-13-public-front-door.md) *(optional)* | public landing, how-it-works, SEO/metadata/OG, guest-browse decision (+ public-endpoint REQs) | 0–2 · product decision |

### Dependency & sequencing

```
Foundation (strictly in order):
  0 design language ──► 1 primitives & states ──► 2 shells & navigation

Area redesigns (after 0–2; sequential recommended, parallel possible across disjoint trees):
  3 auth/first-run
  4 storefront ──► 5 booking ──► 6 checkout/money        (the customer funnel, in funnel order)
  4 ──────────────► 8 nurse business & verification      (reuses phase 4's trust components)
  7 nurse daily ops
  9 customer account · 10 messaging/notifications · 11 admin/partner

Closing:
  12 copy, motion & polish (last — sweeps everything the earlier phases touched)
  13 public front door (any time after 0–2; needs a product decision on guest browse)
```

**Ownership rules that keep parallel runs safe:** phase 0 owns `theme/`, `AppIcon`, `AppButton`, the brand
mark; phase 1 owns every *shared* primitive (`StatusChip`, `CountdownTimer`, `RatingInput`, `StepperHeader`
usage, state views, `<Money>`, the Jalali picker); phase 2 owns `layout/`. Later phases **consume** these and
own only their route tree plus the feature components that belong to it (e.g. `NurseResultCard` → 4,
`BookingRequestSummaryCard` → 5, messaging composites → 10). If a later phase finds a foundation gap, it
extends the foundation file *minimally* and notes it in its report — it does not fork a local variant.

**Minimum path to "the app suddenly looks designed":** phases **0 → 1 → 2**. Those three de-starter every
screen at once (theme pass + primitives + chrome); phases 3–11 are then per-area redesigns on a system that
already looks right.

---

## How the phase files are written

Each file follows the repo's [phase template](../../phases/_shared/phase-template.md): a one-paragraph
mission, context (what already exists — don't rebuild), required reading (including the relevant
[`audit/`](audit/) files, which carry the full evidence), enumerated scope with file paths, the mocks/REQ
posture, the invariants it must not break (including the area's **do-not-regress** list), a Definition of
Done, concrete how-to-test steps across the four axes (`/fa` + `/en` × light + dark, mobile + desktop), and a
close-out (docs + report + memory).

Non-negotiables for every phase (they restate them, but for the human reader):

- **Invoke the `frontend-designer` skill before any UI work** — it is the design contract (brand palette,
  token rules, `App*` wrappers, icon registry, RTL/dark-mode/i18n rules).
- **`npm run check` green + `npm run test:ci`** when shared components change; new shared components get
  co-located tests; `en.json`/`fa.json` stay in sync.
- **Frontend lane only.** Backend gaps become REQ entries in
  [`for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md), never edits to `server/`.
- **Verify visually on `/fa` first** — it is the default locale and RTL.

## Related documents

- [`audit/`](audit/) — the 15 per-area audit reports (current state, problems with evidence, opportunities,
  keep-lists) this chain was synthesized from.
- [../refinement/README.md](../refinement/README.md) — the integration/production chain that preceded this
  one (complete).
- [../../phases/_shared/agent-operating-rules.md](../../phases/_shared/agent-operating-rules.md) — how an
  executing agent works a phase; every phase file links it.
- [.claude/skills/frontend-designer/SKILL.md](../../../.claude/skills/frontend-designer/SKILL.md) — the
  design contract this chain builds on (and extends: phase 0 updates it when tokens/icons change).
- [../../shared-working-context/frontend/requests/for-backend.md](../../shared-working-context/frontend/requests/for-backend.md)
  — the REQ tracker; UI phases append REQ-039+ here.
