Members told us
what's broken.
The audit explains why.
What members are telling us
These themes emerged from open-text responses — members weren't asked about mobile. They volunteered it. The technical audits explain exactly what's causing each experience.
"I find it frustrating and hard to navigate especially from my phone. Lots of glitches and difficulty accessing what's open at a glance."
Booking felt "competitive or stressful" — the #1 single complaint in the survey. Members on mobile can't see or reach the booking action when the keyboard is open.
- No sticky CTA on search/booking flows — keyboard buries the action button
- Forms don't account for soft keyboard — no scroll-to-field on focus
- Touch targets at 40px, below 44px minimum — small buttons under pressure
"I find the system very difficult on my phone."
The most common unprompted complaint. Members can't tell where they are in the app or how to get back. "Difficult to navigate" was named 13 times without being asked.
- No active state in mobile nav drawer — members don't know where they are
- No back navigation in nested flows — destination → detail → back is disorienting
- Native app shows web nav AND native nav simultaneously inside the webview
"I don't like the switching back and forth. I think it should all be on the app."
Members start a task in the app, hit a wall, and open their browser to finish it. The app and the website do the same things — but differently — with no clear ownership.
- Platform ownership undocumented — no decision on what lives where
- Explore and Search built independently in both native and web
- Web runs inside the native app as an undetected webview — no context sharing
"320 residences available in next 90 days but you have to go into each destination residence by residence."
49 members said the booking window misaligns with how they plan travel. The browse experience forces one-by-one exploration with no filter persistence or list view.
- Duplicate Explore surface in native and web — no single source of truth
- No list view — members must tap into each property individually
- Images unoptimized for mobile — slow loads degrade the browse experience
What we fix first
Every action below is tied directly to something members reported. Effort estimates are conservative. All of Week 1 can ship in a single sprint.
- Add sticky CTA to booking + search flows 30 min
- Fix keyboard avoidance — scroll-to-field on form focus 2 hrs
- Raise touch targets 40px → 44px on web buttons + inputs 5 min
- Add loading + error states to booking flows 4 hrs
- Add active state to mobile nav drawer 10 min
- Add webview detection — hide duplicate nav + footer 15 min
- Add back navigation + breadcrumb to nested flows 4 hrs
- Fix native touch targets on icon-only buttons 1 hr
- Document platform ownership — what lives where Decision
- Remove 8 deprecated screens from native app 1 hr
- Build auth bridge for webview context sharing 3 days
- Consolidate Explore to web as single source of truth 3 days
- Optimize Cloudinary images for mobile viewports 1 day
- Add list view to destination browse 3 days
- Add skeleton loading states to browse + search 3 hrs
- Begin unified design token layer (native + web) Ongoing
The audits confirm
what members are saying.
The native app audit and mobile web audit were conducted independently of the survey. They identify the same four root causes members reported — which means the fixes will directly improve member experience.
on mobile
fragmented
stress, not ease
don't work on mobile
Native vs. Web
Platform Decision
Framework
When to use native vs web
Current vs. Recommended
| Feature / Flow | Current | Recommended | Rationale |
|---|---|---|---|
| Login / Auth | Native | Native | Biometrics, secure token storage |
| Bottom tab navigation | Native | Native | Platform convention, instant |
| Home / Dashboard | Web | Native → | Frequently accessed, needs speed |
| Explore / Search | Both ⚠ | Web only | Editorial content, frequently updated |
| Destination details | Both ⚠ | Web only | Rich content, media-heavy |
| Property + Calendar | Native | Web → | High maintenance; web more flexible |
| Available Trips | Web | Web | Forms, changes frequently |
| Book / Request Trip | Web | Web | Form-heavy, needs fewer releases |
| My Trips list | Web | Web | Push notifications from native |
| Trip Details | Web | Web | Complex multi-section layout |
| Get Ready / Pre-trip | Native | Web → | Forms, lots of edge cases |
| Add Traveler | Native | Web → | Form-heavy, editorial |
| Flight Info | Native | Web → | Form-heavy |
| Cancellation | Native | Web → | Policy display + form |
| Connect / Messaging | Native | Native | Real-time, push notifications required |
| Profile / Account | Web | Web | Forms, changes frequently |
| Community | Web | Web | Content-heavy |
| Push Notifications | Native | Native | Requires device integration |
Four-Phase Roadmap
- Document platform map — ratify with product + engineering
- Add webview detection (
isWebviewflag) to The Source v2 - Conditionally hide web nav + footer inside The Club
- Establish shared design token source of truth
- Remove 8 deprecated screens from native app
- Touch targets — 44px minimum enforced on both platforms
- Typography — reduce to 8 semantic sizes, enforce Canela rules
- Sticky CTAs — search, booking, trip detail flows
- Keyboard avoidance — forms on both platforms
- Active nav states — mobile drawer shows active section
- Move Add Traveler → web form (retire native)
- Move Flight Info → web form
- Move Cancellation → web form
- Unify Explore/Search → web only
- Build webview bridge: auth sharing, native back
- Shared design token system across both platforms
- Accessibility remediation — WCAG AA compliance
- Form component library (web) + button library (native)
- Loading/skeleton states — consistent across all screens
- Image optimization — responsive Cloudinary transforms
React Native
iOS + Android
Full Audit
Top 10 Critical Fixes
| # | Issue | Location | Fix |
|---|---|---|---|
| 1 | Color system fragmentation — 70+ colors, 4 error reds, 763 hardcoded values |
src/styles/Theme.ts, all component styles |
Consolidate to ~30 colors, enforce via ESLint, create semantic tokens |
| 2 | Typography chaos — 23 sizes, confusing names, no hierarchy |
src/styles/Theme.ts |
Reduce to 8 sizes max, define semantic tokens (h1/h2/body/caption) |
| 3 | Accessibility absent — 80%+ of interactive elements missing labels/roles |
All components |
Audit every touchable, add labels + roles + hitSlop, test VoiceOver/TalkBack |
| 4 | Inline styles replace design system throughout |
All component files |
ESLint rule against hardcoded px values; extract to StyleSheet.create() outside render |
| 5 | Modal presentation inconsistency + 8 deprecated screens |
src/app-navigation/RootStack.tsx |
Define clear modal rules, remove deprecated screens |
| 6 | Deep linking incomplete — 6 of 26+ routes mapped |
src/app-navigation/linking.ts |
Map all major screens, add URL validation, test both platforms |
| 7 | No form component library — inputs unstyled, no validation feedback |
src/components/ |
Build LabeledInput, FormError, FormSection with consistent focus/error states |
| 8 | Spacing system ignored — 30+ magic numbers override tokens |
All component files |
Add xs (4), xl (48) tokens, ESLint enforcement, refactor to Theme.spacing |
| 9 | Loading/error states inconsistent — hidden in prod, no skeletons 3 members: slow / loading issues reported |
src/components/error-notifier/ |
Show errors in prod (generic message), build SkeletonLoader, disable buttons during submission |
| 10 | Button system fragmented — 3 different heights, BottomButtons hardcoded |
src/components/member/BottomButtons.tsx |
Define button variants (primary/secondary/tertiary), standard height 48, support small/medium/large |
Typography & Color Chaos
3 different greens for status: #1D8139, #718991, #09d9e0
15+ gray shades with confusing naming: light1–6, dark1–3, mediumDark, neutral
Opacity hack:
colors.states.error + '10' instead of proper alpha utility
Screen-by-Screen Findings
Strengths
- Type-safe navigation via RootStackParamList in src/app-navigation/RootStackParam.tsx
- Clear separation of auth states (Loading → Login → Tabs) in RootStack.tsx
- Modal/transparentModal grouping for presentation control
- Deep linking config exists with prefixes theclub:// and thesourcev2://
Issues
- Two modal strategies with no clear pattern — modal vs transparentModal mixed inconsistently
- Only 6 routes mapped via deep link; 20+ key screens unreachable (no property, destination, FlightInfo, Connect, Settings)
- 8 screens still present with _DEPRECATED_ naming (PdfModal, PostTripSurvey, ClubJournal, etc.)
- No beforeRemove listeners — back navigation handling inconsistent
- Untyped any fields in RootStackParam.tsx (e.g., UpdateConfirmation.data)
Login
- Status bar hardcoded white
- Platform-specific font sizes as magic numbers
- No accessibility labels on any buttons
Explore / Search
- Filter button lacks active state visual
- No focus state on search input
- Loading indicator positioned poorly in list items
Destination Screen
- Styling uses hardcoded colors
- Custom modal header inconsistency unclear
Property Screen
- Multiple hardcoded border radius values (15, 8 — not tokens)
- Calendar tappable while data loads (no visual feedback)
- Width calculation calendarWidth/7 magic math
Get Ready / Pre-Trip
- No visual feedback for incomplete checklist sections
- Inconsistent input styling
- Spacing inconsistencies throughout
Cancellation
- colors.states.error + '10' opacity hack
- Hardcoded ambassador photo size (100x100)
- Button sizing inconsistent (minHeight: 50 vs height: 50)
Connect / Messaging
- Unread badge not square (19x17)
- Font hardcoded as 10 instead of Theme token
All Forms
- No consistent input styling — default RN TextInput with inline styling
- Only button disable/enable as feedback — no field-level errors
- keyboardType specified inconsistently
- accessibilityLabel missing on 80%+ of interactive elements (buttons, icons, touchables)
- hitSlop rarely used — most small touch targets (icons, close buttons) below 44x44pt minimum
- No semantic roles — accessibilityRole missing from custom buttons, list items, headings
- brand.light (#cadcde) on white: ~2.8:1 — WCAG AAA failure
- grey.light4 (#77777D) on white: ~4.5:1 — borderline AA
- No live region announcements — filter results update silently
- No evidence of VoiceOver/TalkBack testing
Quick Wins
Next.js Portal
Mobile + Webview
Full Audit
Top 10 Mobile Fixes
| # | Issue | Location | Fix |
|---|---|---|---|
| 1 | Button touch targets below 44px (size-md = 40px) |
components/Common/Button/styles.ts:141 |
Enforce 44px minimum across all interactive elements |
| 2 | No sticky CTA on search/booking flows 64 members: booking felt competitive or stressful |
pages/search.tsx, booking modal |
Add position:sticky bottom container with CTA + safe-area-inset-bottom |
| 3 | No webview detection — redundant nav/footer inside native app 7 members: forced to switch between app and browser |
pages/_app.tsx |
Add isWebview utility; conditionally hide footer, simplify nav |
| 4 | Cloudinary images not optimized for mobile |
All CloudinaryImage components |
Add responsive w_ transforms; cap at 90vw on mobile |
| 5 | Forms don't account for soft keyboard 64 members: booking felt stressful — keyboard buries key actions |
All form pages |
Scroll-to-field on focus; padding-bottom adjustment when keyboard open |
| 6 | Body text at 14px (below mobile minimum) |
styles/theme.ts:122 |
Increase body to 16px on xs/sm breakpoints |
| 7 | No active tab indicator in mobile nav drawer 13 members: difficult to navigate on phone |
components/Nav/index.tsx:456 |
Add background or underline for active nav item |
| 8 | Tab navigation truncation on ≤375px |
TabNav on Profile, MyTrips |
Responsive font size or label abbreviation for xs |
| 9 | Canela used at h6 (16px mobile) |
styles/theme.ts:117 |
Restrict Canela to h1–h3 only; always Gotham below 28px |
| 10 | No back navigation in nested mobile flows 13 members: hard to navigate / find way around on phone |
Nav/drawer stacking |
Add back button/breadcrumb to nested states |
All Findings by Area
Strengths
- Hamburger menu correctly hidden on mobile; drawer pattern appropriate
- Nav height: 64px mobile (reasonable)
- Logo/icon sizing appropriate at 48x48px
Issues
- No active indicator in mobile drawer — display: isMobile ? 'none' : 'flex' hides underline on mobile (Nav/index.tsx line 456)
- No breadcrumb or back path in nested states — destination details → modal → back is disorienting
- Profile icon lacks touch affordance — no visual indication it's tappable on mobile (Nav/index.tsx:496–505)
- Body text at 14px — below 16px minimum for mobile legibility (styles/theme.ts:122–127)
- Canela used at h6 (16px on mobile) — serif at 16px loses elegance; CLAUDE.md requires 28px+ (styles/theme.ts:117–121)
- eyebrowSmall and bodyXSmall at 12px — too small on mobile; no responsive bump (styles/theme.ts:188–193)
- No max line-length constraint — text stretches edge-to-edge on mobile
- No webview detection — footer and full nav render inside webview (redundant, wastes vertical space)
- No native bridge — can't access auth tokens, push notifications, native share
- External links unhandled — may open inside webview instead of external browser
- Android back button conflicts — web history management conflicts with native back
- No testing confirmed inside actual The Club app
Recommended Fix
const isWebview = typeof window !== 'undefined' && !!window.ReactNativeWebView;
- Cloudinary widths not confirmed responsive — images likely fetched at desktop size on mobile (bandwidth waste + layout shift)
- next/Image disabled (images: { unoptimized: true } in next.config.mjs) — mobile optimization is entirely manual
- No loading="lazy" on images below fold
- Carousel images not responsive — full container width, may cause layout shift
- Hero image aspect ratio — constants defined but not verified for correct mobile crop
- Phone input 40px — below 44px minimum (components/Form/InternationalPhoneInput/styles.ts)
- No keyboard avoidance — forms obscured by soft keyboard; no scrollIntoView() or padding adjustment
- Validation errors hidden by keyboard — ErrorStyled renders below input, not above
- No mobile-specific input types — missing type="tel", type="email" for correct keyboards
- TextField marginBottom: 32px hardcoded — excessive on small screens
Quick Wins
All Issues
Ranked by
Impact × Effort
Members agree
with the audit
How members rate the portal and booking
What members say about the app and mobile web
In their own words
The audit and the members agree
How survey data shapes the roadmap
The survey reinforces — and in some cases escalates — the audit's priority assignments. Issues the audit flagged as P1 on technical grounds alone become more urgent when member friction data is layered in.
- 64 members named booking stress
- 13.8% dissatisfied with the portal
- Sticky CTA is an S-effort fix
- Highest impact per sprint day spent
- 7 members frustrated by app/web split
- #3 improvement: "integrate app + portal"
- Document ownership decision publicly
- Single source of truth for feature delivery
- "320 residences, one by one" = planning failure
- 49 members: window misaligns with travel habits
- Browse is the discovery surface — fix it early
- Drives satisfaction more than raw speed