Skip to content

UX/UI Realignment — 2026-04-21

Deep analysis + execution plan. Written after user surfaced 4 screenshots showing FE drift from the locked brainstorm decisions (.impeccable.md + plans/ideony-ux-overhaul-plan.md + canonical blueprint).

Sources of truth in priority order:

  1. .impeccable.md (design context — users, brand, tokens, hard bans)
  2. Canonical blueprint (section “Canonical Blueprint Constraints” in .impeccable.md) — 8 non-negotiable product rules
  3. plans/ideony-mvp0-blueprint.md — screen-by-screen construction plan
  4. plans/ideony-ux-overhaul-plan.md — 2026-04-19 Italian Sole overhaul

1. Root-cause audit (from the 4 screenshots)

Section titled “1. Root-cause audit (from the 4 screenshots)”

Bug A — stray “triage” tab in bottom bar

Section titled “Bug A — stray “triage” tab in bottom bar”
  • File: apps/mobile/components/chrome/TabBar.tsx
  • Cause: custom TabBar iterates state.routes unconditionally and ignores options.href === null. The (consumer)/_layout.tsx layout correctly declares triage with href: null, but the custom bar renders it anyway, falling back to route.name as label → “triage”.
  • Fix: filter routes where descriptors[key].options.href === null (and options.tabBarItemStyle?.display === "none" for parity with upstream).

Bug B — untranslated keys leaking on Home (home.categoryPlumber, etc.)

Section titled “Bug B — untranslated keys leaking on Home (home.categoryPlumber, etc.)”
  • File: apps/mobile/app/(consumer)/index.tsx
  • Cause: keys home.categoryPlumber|Electrician|Hvac|Locksmith|Painter| Carpenter are not present in lib/i18n/en.json or it.json. The JSON file only defines snake-case variants home.category_plumbing| electrical|locksmith|hvac|carpentry|painting (used by CategoryChips).
  • Deeper issue: two code paths declare the same categories with different slug conventions (plumber vs plumbing). Backend expects one canonical list.

Bug C — generic “title” header on a blank screen

Section titled “Bug C — generic “title” header on a blank screen”
  • File: apps/mobile/app/(consumer)/triage.tsx:217
  • Cause: const { t } = useTranslation("triage") — namespace triage is never registered. lib/i18n/index.ts registers only the translation namespace. Result: t("title") returns the raw key.
  • Fix: either drop the namespace argument (useTranslation() + call t("triage.title")), or register triage as a dedicated namespace with a nested resource. Prefer the former (consistent with the rest of the app).

Bug D — two conflicting Home implementations, blueprint violated

Section titled “Bug D — two conflicting Home implementations, blueprint violated”
  • File conflict: app/(consumer)/index.tsx (old category grid) vs app/(consumer)/search.tsx (new prompt-card pattern).
  • Blueprint rule #1: “Home = ChatGPT/Claude-style prompt. Main screen is a large textarea with placeholder ‘Descrivi il tuo problema…’ + photo/ video attachment. NOT a category grid, NOT a traditional search bar.
  • Current state:
    • Home tab (index.tsx) = rejected pattern (grid + search bar).
    • Search tab (search.tsx) = correct pattern (PromptCard + CategoryChips + MapAffordanceStrip).
  • Fix: promote search.tsx content to Home; delete or redirect the legacy index.tsx grid. “Search” tab either goes away or becomes a pure category/filter browser surface.

Bug E — typography silently falls back to system font

Section titled “Bug E — typography silently falls back to system font”
  • Files: app/(consumer)/search.tsx, components/home/CategoryChips.tsx, MapAffordanceStrip.tsx, PromptCard.tsx.
  • Cause: app/_layout.tsx loads fonts as "Gambarino-Regular", "Switzer-Regular", "Switzer-Semibold", etc. (hyphenated weight suffix). Several screens reference fontFamily: "Gambarino" or fontFamily: "Switzer" — these family names are not in the useFonts manifest, so they silently resolve to system serif/sans.
  • Fix: pick a convention:
    • (preferred) keep hyphenated weight-suffixed families and rename every usage to "Switzer-Regular" / "Switzer-Semibold" / "Gambarino- Regular".
    • OR register alias family names in useFonts that point to the same ttfs (Switzer: … Switzer-Regular.ttf) and let fontWeight pick the weight — simpler but fragile on web.
  • Typography drift = Italian Sole is invisible on the screens that were supposed to showcase it most.
  • Consumer search chips use emoji; tab bar + profile use Phosphor duotone; triage header + MapAffordanceStrip use lucide. The result is a visually inconsistent icon voice.
  • .impeccable.md is silent on emoji-in-chips, but CLAUDE.md states “lucide-react-native (inline) + phosphor-react-native (duotone for nav/tabs)”. Emoji is a third axis that snuck in.
  • Fix: decide one of:
    • keep emoji for category chips only (warm, Italian, Airbnb-like) and ban them everywhere else; OR
    • swap chips to Phosphor duotone (Wrench, Lightning, Key, Snowflake, Hammer, PaintBrush) for visual consistency with tab icons.

Bug G — category slug drift between FE and BE

Section titled “Bug G — category slug drift between FE and BE”
  • index.tsxplumber|electrician|hvac|locksmith|painter|carpenter
  • CategoryChipsplumbing|electrical|locksmith|hvac|carpentry|painting
  • API seed + search filter expects one canonical list. Follow-up task #161 (Milano seed) already flagged this drift. Decision needed + propagate through seed, search filter DTO, triage planner, AI model prompt.

#WhereProblemSeverity
1components/chrome/Header.tsx (Home index) vs components/home/HomeTopBar.tsx (Search)Two distinct top-bar components — inconsistent IAHIGH
2Category chip copy: chip is both the label and the filter state indicator, but no “Clear” affordance when selectedUXMEDIUM
3MapAffordanceStrip shows {prosNearby} count only when permission granted; no skeleton while the count is loadingUXLOW
4Search tab greeting uses firstName — but Clerk’s user.firstName may be null for users who signed up with phone onlyi18n + dataMEDIUM
5Profile screen theme toggle ships Light/Dark/System but CC1 theme store (task #150) is still pending — toggle is a no-op visuallyUXMEDIUM
6Chevron in “Explore on map” renders at 16px with strokeWidth=2 inside a 20px container — visually weakpolishLOW
7PromptCard placeholder copy vs blueprint copy — needs spot-check against spec oncecopyLOW

Findings 1–7 should be swept alongside the A–G fixes.


Blueprint + .impeccable.md also imply BE work that the current MVP0 hasn’t closed:

  • B1. Canonical category slug list (pick plumbing|electrical| hvac|locksmith|carpentry|painting — matches Stripe trade taxonomy convention). Propagate through: Prisma seed, Category table enum, SearchController filter DTO, TriageService AI prompt JSON schema, E2E fixtures.
  • B2. Ensure /categories endpoint returns the canonical list for FE consumption — FE should fetch it, not hardcode.
  • B3. Demo seed must include Rome-based pros (for the 41.9028, 12.4964 E2E geolocation mock) OR E2E must mock Milano. Task #161 already tracks this.
  • B4. Search results schema returned by BE must match the two-pricing-mode contract (“€50 fisso” vs “€45/h”) — blueprint #5. Verify ProfessionalListItemDto has pricingMode: "FLAT"|"HOURLY" and a priceAmount field.
  • B5. Verificato badge requires verificationStatus: "VERIFIED"| "PENDING"|"UNVERIFIED" on professional payload. Trust score already lives on ProfessionalProfile (trustTier). Confirm FE uses the tier not just a boolean.

4. Execution plan — Sprint “Realignment”

Section titled “4. Execution plan — Sprint “Realignment””
  1. FE renders Italian Sole typography everywhere (Bug E)
  2. Home is the ChatGPT-style prompt (Bug D)
  3. Zero raw i18n keys visible (Bug B, C)
  4. Zero stray nav items (Bug A)
  5. One icon voice per surface (Bug F)
  6. FE + BE agree on category slugs (Bug G / B1–B2)
  7. Verificato badge wired through real trustTier payload (B5)

Parallelization plan (max 8 concurrent, cap per memory)

Section titled “Parallelization plan (max 8 concurrent, cap per memory)”

Batch 1 — blockers (parallel 4 agents):

  • R1 Fix TabBar href: null + delete legacy Home grid + promote search.tsx to Home tab (or rename routes). One agent — the routing surface is tightly coupled.
  • R2 Font-family rename codemod — replace bare "Gambarino" / "Switzer" with "Gambarino-Regular" / "Switzer-Regular" etc. across apps/mobile/**/*.tsx. Structural pass with ast-grep.
  • R3 i18n key backfill — add missing home.category* keys in en.json + it.json; fix triage.tsx namespace usage; sweep for any other raw keys leaking at runtime.
  • R4 Category slug canonicalization — pick slug list, run find+replace in FE, ensure BE seed + DTO + triage prompt match.

Batch 2 — design polish (parallel 3 agents, after Batch 1 lands):

  • R5 Icon voice cleanup — Phosphor for nav/chrome, lucide for inline, emoji only in consumer category chips (or full swap to Phosphor if decided). Document in .impeccable.md.
  • R6 Verificato badge on ProCard + search results (wire to trustTier). Blueprint #4.
  • R7 Two-pricing-mode visual differentiation on ProCard. Blueprint #5.

Batch 3 — verification (serial, after Batch 2):

  • R8 Ship accessibility-compliance:ui-visual-validator run against the 7-screen hero flow + SOS flow; save screenshots; attach to PR.
  • R9 Re-run only the E2E scenarios whose selectors changed (scenario-01 consumer book, scenario-02 SOS). Update fixtures.
  • Dark mode codemod (task #150, #151) — still Phase 2, post-demo
  • Clerk 2FA dashboard disable (#140) — manual user action pre-demo

E2E bucket — DEFERRED (user explicit 2026-04-21)

Section titled “E2E bucket — DEFERRED (user explicit 2026-04-21)”

All new E2E work paused. Resume ONLY when user says so.

idTaskStateNotes
#152Phase C — 6 canonical flows✅ complete 2026-04-20Selectors need refresh post-R1 → #175
#175R9 — E2E selector refresh after route rename🟡 part of realignment Batch 3Fixes broken selectors from Home/SOS/Chat changes
#153Phase A — CI gate + ~10 more single-actor flows🔴 pausedDispatch only after #175 + #174 green
#154Phase B — multi-role choreography🔴 pausedDouble blocked on BE test-tenancy APIs (own spec plans/specs/2026-04-20-phase-e-multi-role-e2e-spec.md)

Resume order: R9 #175 → Phase A #153 → Phase B #154 (last, needs BE work first).

In-flight agents (let them finish; do NOT spawn dependents)

Section titled “In-flight agents (let them finish; do NOT spawn dependents)”
  • addbe0eb6815731f9 — CC7b pro/components icon codemod
  • a868c5b6efe5ecd7f — SOS category + Milano seed fix
  • a9811ef65be293957 — TS drift + push

Their deltas may touch files we’ll edit in Batch 1 — wait for completion before Batch 1 dispatch, then resolve conflicts.


idrisklikelihoodimpactmitigation
RR1Routing change (Search → Home) breaks deep links + E2E selectorsMHupdate 6 E2E specs; leave legacy redirect for 1 release
RR2Font rename codemod misses NativeWind className="font-*" usagesMMast-grep grep + Lost Pixel diff before merge
RR3Category slug change de-syncs local demo DBLMpnpm db:seed reset in local dev only (autonomous OK)
RR4BE /categories endpoint missing → FE falls back to hardcoded listMMadd endpoint OR keep FE list + add contract test
RR5TabBar filter regression hides a legitimate routeLHassert 5 tabs exact in unit test + visual diff

6. Success criteria (what “aligned” means)

Section titled “6. Success criteria (what “aligned” means)”
  • Home tab opens the prompt-card flow (textarea + chips + map)
  • No route named “triage” visible in tab bar
  • All visible text localised (IT default, EN fallback), zero raw keys
  • All body text uses Switzer-* family; all headings use Gambarino-Regular — verified by on-device screenshot against Fontshare specimen
  • Category slugs identical in FE mock, BE seed, triage AI JSON schema
  • Verificato badge renders on at least one real pro profile in local demo
  • Two pricing modes (FLAT vs HOURLY) visually distinguishable on ProCard
  • 6 E2E scenarios green locally after route rename

  • Q1 slugstrade: plumbing/electrical/hvac/locksmith/carpentry/painting
  • Q2 chipsFluent UI Emoji (Microsoft, MIT, 3D-rendered, closest public analogue to Airbnb’s private 2022 categories bar). Post-seed swap to curated Italian illustrations (.impeccable.md open decision #1).
  • Q3 Search tabdelete. Tab order: Home / Bookings / SOS (center-raised) / Chat / Profile. Per plans/specs/2026-04-20-ux-phase-c-design.md:35 already locked.
  • Q4 typography-Regular suffix families (explicit weight), fontFamily renamed. Matches iOS + Android + Web identically, avoids font-synthesis quirks.

8. New tasks surfaced (2026-04-21 iteration)

Section titled “8. New tasks surfaced (2026-04-21 iteration)”
  • R10 (#176) — Home PromptCard true GPT/Claude chat-UX: focus-expand, suggestion chips, recent prompts, placeholder cycle, streaming typing, sticky-to-keyboard. Current impl is static; spec line 113 of Phase A said “expands to full-screen input on focus”.
  • R11 (#177) — SOS center-raised tab per Phase C spec §1.1: translateY -10, 32px halo (olive rest, terracotta focused), pulse when urgency within 10min. Replaces generic R1 tabbar fix.
  • R12 (#178) — Fluent Emoji asset pipeline: 6 PNGs bundled to assets/icons/categories/, CategoryIcon wrapper component.
Batch 1 (4 parallel):
R1 #167 Home promotion
R2 #168 font codemod [blocked by R1]
R3 #169 i18n backfill + triage ns
R4 #170 canonical slugs FE+BE+AI
Batch 2 (4 parallel):
R5 #171 icon voice [R1+R3+R4+R12]
R6 #172 Verificato badge [R4]
R7 #173 two pricing modes [R4]
R11 #177 SOS center-raised tab
R12 #178 Fluent emoji assets [R4]
Batch 3 (1 serial, depends heavy):
R10 #176 Home chat-UX parity [R1+R2+R3]
R9 #175 E2E selector refresh [R1]
Batch 4 (serial, verification):
R8 #174 visual validator [all above]