UX/UI Realignment — 2026-04-21
UX/UI Realignment — 2026-04-21
Section titled “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:
.impeccable.md(design context — users, brand, tokens, hard bans)- Canonical blueprint (section “Canonical Blueprint Constraints” in
.impeccable.md) — 8 non-negotiable product rules plans/ideony-mvp0-blueprint.md— screen-by-screen construction planplans/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
TabBariteratesstate.routesunconditionally and ignoresoptions.href === null. The(consumer)/_layout.tsxlayout correctly declarestriagewithhref: null, but the custom bar renders it anyway, falling back toroute.nameas label → “triage”. - Fix: filter routes where
descriptors[key].options.href === null(andoptions.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| Carpenterare not present inlib/i18n/en.jsonorit.json. The JSON file only defines snake-case variantshome.category_plumbing| electrical|locksmith|hvac|carpentry|painting(used byCategoryChips). - Deeper issue: two code paths declare the same categories with
different slug conventions (
plumbervsplumbing). 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")— namespacetriageis never registered.lib/i18n/index.tsregisters only thetranslationnamespace. Result:t("title")returns the raw key. - Fix: either drop the namespace argument (
useTranslation()+ callt("triage.title")), or registertriageas 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) vsapp/(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).
- Home tab (
- Fix: promote
search.tsxcontent to Home; delete or redirect the legacyindex.tsxgrid. “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.tsxloads fonts as"Gambarino-Regular","Switzer-Regular","Switzer-Semibold", etc. (hyphenated weight suffix). Several screens referencefontFamily: "Gambarino"orfontFamily: "Switzer"— these family names are not in theuseFontsmanifest, 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
useFontsthat point to the same ttfs (Switzer: … Switzer-Regular.ttf) and letfontWeightpick the weight — simpler but fragile on web.
- (preferred) keep hyphenated weight-suffixed families and rename every
usage to
- Typography drift = Italian Sole is invisible on the screens that were supposed to showcase it most.
Bug F — icon-library split-brain
Section titled “Bug F — icon-library split-brain”- 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.mdis silent on emoji-in-chips, butCLAUDE.mdstates “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.tsx→plumber|electrician|hvac|locksmith|painter|carpenterCategoryChips→plumbing|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.
2. Secondary drift findings (deeper scan)
Section titled “2. Secondary drift findings (deeper scan)”| # | Where | Problem | Severity |
|---|---|---|---|
| 1 | components/chrome/Header.tsx (Home index) vs components/home/HomeTopBar.tsx (Search) | Two distinct top-bar components — inconsistent IA | HIGH |
| 2 | Category chip copy: chip is both the label and the filter state indicator, but no “Clear” affordance when selected | UX | MEDIUM |
| 3 | MapAffordanceStrip shows {prosNearby} count only when permission granted; no skeleton while the count is loading | UX | LOW |
| 4 | Search tab greeting uses firstName — but Clerk’s user.firstName may be null for users who signed up with phone only | i18n + data | MEDIUM |
| 5 | Profile screen theme toggle ships Light/Dark/System but CC1 theme store (task #150) is still pending — toggle is a no-op visually | UX | MEDIUM |
| 6 | Chevron in “Explore on map” renders at 16px with strokeWidth=2 inside a 20px container — visually weak | polish | LOW |
| 7 | PromptCard placeholder copy vs blueprint copy — needs spot-check against spec once | copy | LOW |
Findings 1–7 should be swept alongside the A–G fixes.
3. Backend + mock alignment
Section titled “3. Backend + mock alignment”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
/categoriesendpoint 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
ProfessionalListItemDtohaspricingMode: "FLAT"|"HOURLY"and apriceAmountfield. - B5. Verificato badge requires
verificationStatus: "VERIFIED"| "PENDING"|"UNVERIFIED"on professional payload. Trust score already lives onProfessionalProfile(trustTier). Confirm FE uses the tier not just a boolean.
4. Execution plan — Sprint “Realignment”
Section titled “4. Execution plan — Sprint “Realignment””Sprint goals (in order)
Section titled “Sprint goals (in order)”- FE renders Italian Sole typography everywhere (Bug E)
- Home is the ChatGPT-style prompt (Bug D)
- Zero raw i18n keys visible (Bug B, C)
- Zero stray nav items (Bug A)
- One icon voice per surface (Bug F)
- FE + BE agree on category slugs (Bug G / B1–B2)
- Verificato badge wired through real
trustTierpayload (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 + promotesearch.tsxto 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. acrossapps/mobile/**/*.tsx. Structural pass with ast-grep. - R3 i18n key backfill — add missing
home.category*keys inen.json+it.json; fixtriage.tsxnamespace 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-validatorrun 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.
Out of scope for this sprint
Section titled “Out of scope for this sprint”- 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.
| id | Task | State | Notes |
|---|---|---|---|
| #152 | Phase C — 6 canonical flows | ✅ complete 2026-04-20 | Selectors need refresh post-R1 → #175 |
| #175 | R9 — E2E selector refresh after route rename | 🟡 part of realignment Batch 3 | Fixes broken selectors from Home/SOS/Chat changes |
| #153 | Phase A — CI gate + ~10 more single-actor flows | 🔴 paused | Dispatch only after #175 + #174 green |
| #154 | Phase B — multi-role choreography | 🔴 paused | Double 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 codemoda868c5b6efe5ecd7f— SOS category + Milano seed fixa9811ef65be293957— TS drift + push
Their deltas may touch files we’ll edit in Batch 1 — wait for completion before Batch 1 dispatch, then resolve conflicts.
5. Risk matrix
Section titled “5. Risk matrix”| id | risk | likelihood | impact | mitigation |
|---|---|---|---|---|
| RR1 | Routing change (Search → Home) breaks deep links + E2E selectors | M | H | update 6 E2E specs; leave legacy redirect for 1 release |
| RR2 | Font rename codemod misses NativeWind className="font-*" usages | M | M | ast-grep grep + Lost Pixel diff before merge |
| RR3 | Category slug change de-syncs local demo DB | L | M | pnpm db:seed reset in local dev only (autonomous OK) |
| RR4 | BE /categories endpoint missing → FE falls back to hardcoded list | M | M | add endpoint OR keep FE list + add contract test |
| RR5 | TabBar filter regression hides a legitimate route | L | H | assert 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 (
FLATvsHOURLY) visually distinguishable on ProCard - 6 E2E scenarios green locally after route rename
7. Decisions locked 2026-04-21
Section titled “7. Decisions locked 2026-04-21”- Q1 slugs → trade:
plumbing/electrical/hvac/locksmith/carpentry/painting - Q2 chips → Fluent 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 tab → delete. Tab order: Home / Bookings / SOS (center-raised) / Chat / Profile. Per
plans/specs/2026-04-20-ux-phase-c-design.md:35already locked. - Q4 typography →
-Regularsuffix 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/,CategoryIconwrapper component.
9. Execution — final dependency graph
Section titled “9. Execution — final dependency graph”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]