Mobile Responsiveness Enhancement Plan — Lifehub
Master, this plan outlines the path to make Lifehub fully responsive and mobile-first. The Force of DRY patterns guides every decision here.
1. Current State Audit
What Already Works Well
mainusesp-4 lg:p-6— appropriate gutter switch between mobile and desktop- Most KPI grids already use
grid-cols-2 lg:grid-cols-4— stacks gracefully - The layout already separates
_footer_nav.html.erb(mobile) from the desktop navbar in_navbar.html.erb - The mobile sidebar drawer (
_sidebar.html.erb) exists with a hamburger trigger - Background, card, and border colours are already CSS variables — easy consistent theming
- Modal has
mx-4on mobile andmax-h-[90vh] overflow-hidden— a solid start
What Needs Fixing
| Area | Issue |
|---|---|
| Bottom nav | Icons only, no labels, tiny size-6 touch targets, no "More" overflow menu, no bottom safe-area padding |
| Main content | No bottom padding on mobile — content hidden under bottom nav |
| Modal | Stays centre-anchored on mobile — should slide up as a bottom sheet on phones |
| Action headers | Multiple buttons in page headers overflow on narrow viewports |
| Data tables | Tables (accounts, balance_registries, investments) have no overflow-x-auto wrapper |
| Charts | No explicit min-height on mobile — can collapse |
| Forms in modals | Inputs, selects and date fields too small for comfortable touch |
| Tabs & filter pills | Overflow is clipped rather than horizontally scrollable |
| Dashboard patrimony card | min-w-[280px] forces horizontal scroll on small phones |
| Analytics & planning | Dense charts and tables untouched for mobile |
| FAB pattern | No floating action button — primary "new" actions hard to reach |
2. Design Principles
- Mobile-first Tailwind classes — write
sm:/md:/lg:as enhancement, not the baseline. - DRY via shared partials — every repeated pattern (page header, KPI card, table wrapper, bottom sheet) becomes a reusable partial.
- Bottom-sheet modals on mobile — modals on
< smviewports slide up from the bottom (iOS/Android native feel). - 44 px minimum touch targets — all interactive elements meet WCAG 2.5.5.
- Safe-area insets — use
pb-safe(env(safe-area-inset-bottom)) on sticky bars so notch devices aren't clipped. - Consistent CSS variable tokens — all colours stay on
var(--bg-*),var(--text-*),var(--sidebar-*).
3. Shared Component Upgrades (DRY Foundation)
3.1 _footer_nav.html.erb — Enhanced Bottom Navigation Bar
Goal: Promote to a proper bottom navigation bar with labelled icons, active state, "More" overflow drawer, and safe-area padding.
New structure:
┌─────────────────────────────────────┐
│ 🏠 💸 ✅ 📈 ⋯ │
│ Home Expenses Habits Invest More│
└─────────────────────────────────────┘
safe-area-inset-bottom
Changes:
- Items: Dashboard · Expenses · Habits · Investments · More (drawer)
- "More" opens a mini drawer (Turbo Frame or Stimulus) showing: Goals, Analytics, Accounts, Simulator, Planning, Sports, Notes, Market Lists, Birthdays, Countdowns, Construction, Converter, Gamification
- Each item = flex column (icon + label),
min-w-[44px] min-h-[44px] - Active item: coloured icon + white label, inactive:
text-gray-500 pb-[env(safe-area-inset-bottom)]+pb-3fallback on the nav itself- Pin to
fixed bottom-0 left-0 right-0(not sticky — safer on mobile browsers) - New Stimulus controller:
bottom-nav-controller.js— handles "More" toggle
DRY helper to reuse: nav_link helper already exists — extend it to support label: argument and render the vertical layout.
Relevant file: app/views/shared/_footer_nav.html.erb
3.2 _turbo_modal.html.erb — Bottom Sheet on Mobile
Goal: On mobile (< sm), the modal slides up from the bottom like a native sheet; on sm+ it remains centre-anchored.
Changes:
- Add
sm:items-center items-endto the backdrop flex container — modal anchors to bottom on mobile, centre on desktop. - Modal wrapper: remove
mx-4fixed margin; usew-full sm:w-auto sm:mx-6 md:mx-8 - Add
rounded-b-none rounded-t-2xl sm:rounded-2xlto the card - Add drag-handle indicator (
w-10 h-1 bg-white/20 rounded-full mx-auto mb-2) visible on mobile only - Reduce
max-hon mobile:max-h-[85vh] sm:max-h-[90vh] - Animation:
translate-y-full → translate-y-0on mobile (slide up),scale-95 opacity-0 → scale-100 opacity-100on desktop - Add
pb-[env(safe-area-inset-bottom)]to modal body on mobile
Relevant file: app/views/shared/_turbo_modal.html.erb
3.3 _page_header.html.erb — Responsive Header Pattern
Goal: Standardise how every feature page renders its title + action buttons, making them stack vertically on mobile with full-width CTA buttons.
Changes:
- Two-line layout on mobile: title row, then actions row (
flex-col sm:flex-row) - Action buttons go
w-full sm:w-autoon mobile - Title font:
text-xl sm:text-2xl - Expose
mobile_actions:local to allow a compact "+" FAB on mobile when multiple actions exist
Relevant file: app/views/shared/_page_header.html.erb
3.4 New Shared Partial — _table_scroll_wrapper.html.erb
Goal: Wrap every data table in a scrolable container to prevent horizontal overflow breaking layouts.
<%# app/views/shared/_table_scroll_wrapper.html.erb %>
<div class="overflow-x-auto -mx-4 sm:mx-0 rounded-xl">
<div class="min-w-[540px] sm:min-w-0">
<%= yield %>
</div>
</div>
Apply to: accounts table, balance registries table, investments table, analytics tables.
3.5 New Shared Partial — _fab.html.erb (Floating Action Button)
Goal: On mobile, replace multiple header buttons with a single FAB that reveals a speed-dial if multiple actions exist.
<%# app/views/shared/_fab.html.erb %>
<%# Usage: render "shared/fab", actions: [{label:, path:, color:, icon:}] %>
<div class="fixed bottom-20 right-4 z-30 lg:hidden" data-controller="fab">
...speed-dial buttons...
</div>
Apply to: accounts (2 actions), dashboard (2 quick actions), balance registries.
3.6 Tailwind main Padding — Bottom Clearance
Goal: Ensure content isn't hidden under the bottom nav.
Change in layouts/application.html.erb:
<main class="flex-grow p-4 pb-24 lg:pb-6 lg:p-6 ...">
pb-24 gives 96px clearance which safely clears the bottom nav + FAB on mobile.
3.7 Filter Pills / Tab Rows — Horizontal Scroll
Goal: All flex gap-2 flex-wrap pill rows become horizontally scrollable on mobile (no wrap = no reflow jank).
Change pattern (applied consistently across expenses, investments, balance registries, habits, analytics):
<%# Before: %>
<div class="flex flex-wrap gap-2 mb-5">
<%# After: %>
<div class="flex gap-2 mb-5 overflow-x-auto pb-1 scrollbar-none">
<%# Each pill gets flex-shrink-0 %>
Add CSS utility once in app/assets/stylesheets/application.css:
.scrollbar-none { scrollbar-width: none; }
.scrollbar-none::-webkit-scrollbar { display: none; }
4. Feature-by-Feature View Changes
4.1 Dashboard (organizations/dashboard/index.html.erb)
| Issue | Fix |
|---|---|
min-w-[280px] patrimony card forces scroll on 320px phones |
Remove min-w, use w-full lg:w-auto |
KPI table (table element) not scrollable |
Wrap in _table_scroll_wrapper |
| Quick actions buttons in header overflow | Move to FAB (_fab.html.erb) on mobile |
lg:grid-cols-[auto_1fr] — already good |
✅ No change needed |
Bottom charts row uses grid-cols-1 lg:grid-cols-2 |
✅ Already stacks |
4.2 Accounts (organizations/accounts/index.html.erb)
| Issue | Fix |
|---|---|
| Header has 2 action buttons that may wrap | Consolidate into _page_header + FAB on mobile |
| Accounts data table has many columns | Wrap with _table_scroll_wrapper |
| Balance registries table | Same wrapper |
| Drag-to-sort on account cards | Add note: "drag-sort disabled on touch — reorder via edit on mobile" (future: touch-drag handle) |
4.3 Expenses (organizations/expenses/index.html.erb)
| Issue | Fix |
|---|---|
| Month filter pills wrap and reflow | Convert to horizontal scroll row |
| Expense list rows have too much info causing overflow | Truncate description on mobile (truncate max-w-[120px] sm:max-w-none) |
Summary cards (grid-cols-3) too narrow on 320px |
Change to grid-cols-1 sm:grid-cols-3 |
| "New Expense" button | FAB on mobile |
4.4 Investments (organizations/investments/index.html.erb)
| Issue | Fix |
|---|---|
KPI cards grid-cols-2 lg:grid-cols-4 |
✅ Already responsive |
| Tab filter pills (CDB, LCI, etc.) overflow | Convert to horizontal scroll row |
| Investment table (Lista tab) | Wrap with _table_scroll_wrapper |
| Timeline chart | Ensure min-h-[200px] on mobile; chart.js maintainAspectRatio: false already needs to be set |
| "New Investment" button | FAB on mobile |
4.5 Goals (organizations/goals/index.html.erb)
| Issue | Fix |
|---|---|
grid-cols-1 md:grid-cols-2 goal cards |
✅ Good — stacks on mobile |
KPI cards in detail section grid-cols-2 lg:grid-cols-4 |
✅ Good |
| Chart section may be too wide | Chart wrapper: w-full overflow-hidden |
4.6 Analytics (organizations/analytics/index.html.erb)
| Issue | Fix |
|---|---|
4 KPI cards grid-cols-2 lg:grid-cols-4 |
✅ Good |
Charts grid grid-cols-1 lg:grid-cols-2 |
✅ Good — verify chart height minimum |
| Analytics tables (heatmap etc.) | Wrap with _table_scroll_wrapper |
| Dense data sections need visual breathing room | Add mb-6 separator sections on mobile |
4.7 Habits (organizations/habits/index.html.erb)
| Issue | Fix |
|---|---|
grid-cols-1 md:grid-cols-3 stat cards |
✅ Good |
| Habit cards list — each habit needs minimum 48px height touch target | Ensure py-3 minimum on each habit row |
| "Check In" toggle buttons | Minimum size-10 touch target |
| Habit analytics page charts | Ensure responsive chart heights |
4.8 Sports & Workout Sessions
| Issue | Fix |
|---|---|
| Sports index — list layout | Add overflow-x-auto to any stats table |
| Workout sessions list | Stack sessions vertically — remove any fixed widths |
| Show page metric cards | Verify grid-cols-2 min on mobile |
4.9 Notes (organizations/notes/)
| Issue | Fix |
|---|---|
| Notes grid | Ensure grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 |
| Note show page prose | Max width max-w-prose already responsive |
| Rich text editor (if any) | Ensure min-h-[200px] on mobile |
4.10 Market Lists (organizations/market_lists/)
| Issue | Fix |
|---|---|
| Shopping list items | Minimum 44px row height for checkbox tap |
| Item count badges | Readable at text-xs — fine |
4.11 Birthdays / Countdowns
| Issue | Fix |
|---|---|
| Cards grid | Use grid-cols-1 sm:grid-cols-2 |
| Date countdown numbers | text-2xl readable on mobile |
4.12 Construction Projects
| Issue | Fix |
|---|---|
| Project phases table | Wrap with _table_scroll_wrapper |
| Expenses list | Stack items on mobile |
| Progress bar section | Full-width bar is already w-full |
4.13 Currency Converter
| Issue | Fix |
|---|---|
| Converter form layout | flex-col on mobile, flex-row on sm+ |
| Input size | w-full on mobile |
4.14 Gamification Pages
| Issue | Fix |
|---|---|
| Achievements grid | grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 |
| Leaderboard table | Wrap with _table_scroll_wrapper |
| XP progress bars | Already percentage-based — fine |
4.15 Simulator & Planning
| Issue | Fix |
|---|---|
| Simulator sliders | Ensure w-full input range elements |
| Result chart | Responsive height: h-[250px] sm:h-[400px] |
| Planning table | Wrap with _table_scroll_wrapper |
| Projection inputs stacked inside card | flex-col on mobile |
4.16 Finance Settings (organizations/finance_settings/show.html.erb)
| Issue | Fix |
|---|---|
| Settings form sections | Already likely max-w-2xl — good |
| Input groups | flex-col sm:flex-row for label+input pairs |
| Family management section | Stack member list vertically |
4.17 Memberships / Invitations
| Issue | Fix |
|---|---|
| Members list | Already uses card-based layout — verify 44px row heights |
| Invite form | w-full email input |
4.18 Auth Pages (Devise)
| Issue | Fix |
|---|---|
| Login / Register | Centred card already responsive — verify px-4 on mobile |
| Magic link page | Same treatment |
| Invitation accept | Form inside a card — mx-4 or px-4 safety margin needed |
4.19 Landing Page / Static Pages
| Issue | Fix |
|---|---|
| Landing hero section | Verify text-4xl sm:text-6xl headings |
| Pricing cards | grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 |
| Feature sections | Alternating layout: flex-col sm:flex-row |
| Navbar (static layout) | Hamburger menu for mobile — verify _navbar.html.erb in static/ |
5. Bottom Navigation — Detailed Spec
Items (5 tabs + More)
| Tab | Icon (SVG) | Route |
|---|---|---|
| Home | House / dashboard icon | dashboard_path |
| Expenses | Receipt / money icon | expenses_path |
| Habits | Checkmark circle | habits_path |
| Invest | Chart / trending up | investments_path |
| More | Grid 2x2 / apps icon | Opens "More" drawer |
"More" Drawer — Items
Categorised in two rows of 4:
Row 1: Goals · Analytics · Accounts · Simulator
Row 2: Sports · Notes · Market Lists · Gamification
Footer links: Planning · Birthdays · Countdowns · Construction · Converter · Settings
Visual Design
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ 🏠 │ │ 💸 │ │ ✅ │ │ 📈 │ │ ⋯⋯ │ │
│ │ Home │ │Spend │ │Habits│ │Invest│ │ More │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │
│ safe-area-inset-bottom │
└─────────────────────────────────────────────────────────────────┘
- Active tab: icon uses
text-blue-400, labeltext-[11px] font-semibold text-white - Inactive:
text-gray-500 dark:text-gray-600for both - Active indicator: 2px top border
border-t-2 border-blue-400on the tab - Background:
var(--bg-sidebar)withborder-t: 1px solid var(--sidebar-border) - Height:
~52px+env(safe-area-inset-bottom)padding
Stimulus Controller: bottom-nav-controller.js
// app/javascript/controllers/bottom_nav_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["drawer", "backdrop"]
openMore() {
this.drawerTarget.classList.remove("translate-y-full")
this.backdropTarget.classList.remove("hidden")
}
closeMore() {
this.drawerTarget.classList.add("translate-y-full")
this.backdropTarget.classList.add("hidden")
}
}
"More" drawer — slides up from below the bottom nav:
┌─────────────────────────────────────┐
│ ╌╌ (drag handle) ╌╌ │ ← rounded-t-2xl
│ │
│ 🎯 Goals 📊 Analytics │
│ 🏦 Accounts 📐 Simulator │
│ │
│ ⚽ Sports 📝 Notes │
│ 🛒 Market ⚡ Gamification │
│ │
│ ─ Planning · Birthdays · Converter │
└─────────────────────────────────────┘
6. Implementation Order (Sprints)
Sprint 1 — Foundation (no visual regressions)
- Add
pb-24 lg:pb-6tomaininlayouts/application.html.erb - Create
_table_scroll_wrapper.html.erbpartial - Apply table scroll wrapper to: accounts, balance registries, investments, analytics
- Add
.scrollbar-noneCSS utility toapplication.css - Convert all filter pill rows to
overflow-x-auto scrollbar-none(expenses, investments, analytics, habits)
Sprint 2 — Modal Bottom Sheet
- Update
_turbo_modal.html.erb— bottom sheet on mobile, centred on desktop - Update modal animation classes
- Add safe-area padding to modal body
- Test all forms that open in modals: expenses, investments, goals, habits, accounts, balance registries
Sprint 3 — Bottom Navigation Bar
- Rewrite
_footer_nav.html.erbwith 5-tab layout + "More" drawer - Create
bottom_nav_controller.jsStimulus controller - Register controller in
app/javascript/controllers/index.js - Update
nav_linkhelper (or createbottom_nav_itemhelper) to render icon + label + active state
Sprint 4 — Page Header & FAB
- Update
_page_header.html.erbto supportmobile_actions:+ stack layout - Create
_fab.html.erbshared partial - Create
fab_controller.jsStimulus controller (speed-dial toggle) - Apply FAB to: accounts, dashboard, expenses, investments, goals
Sprint 5 — Feature Page Fixes
- Dashboard: remove
min-w-[280px], wrap KPI table - Expenses: fix summary cards grid, fix overflow
- Investments: wrap table, fix filter pills
- Analytics: wrap tables, verify chart heights
- Goals: verify chart container overflow
- Habits: verify touch targets
- Sports / Notes / Market Lists / Birthdays / Countdowns: grid fixes
Sprint 6 — Auth & Static Pages
- Devise pages: verify padding and form widths
- Landing page: hero heading sizes, pricing grid, feature alternating layout
- Static navbar hamburger
Sprint 7 — Polish & QA
- Safe-area insets audit on iPhone SE (375px) and iPhone 14 (390px)
- Touch target audit — all interactive elements ≥ 44px
- Horizontal scroll regression test — no page should scroll horizontally
- Dark mode regression test on mobile
- Turbo navigation regression — bottom nav active state updates on navigate
7. CSS Tokens & Shared Utilities to Add
In app/assets/stylesheets/application.css (or app/assets/tailwind/application.css):
/* Safe-area bottom padding */
.pb-safe {
padding-bottom: env(safe-area-inset-bottom, 0px);
}
/* Hide scrollbar but keep functionality */
.scrollbar-none {
scrollbar-width: none;
-ms-overflow-style: none;
}
.scrollbar-none::-webkit-scrollbar {
display: none;
}
/* Bottom sheet slide-up animation */
.slide-up-enter {
transform: translateY(100%);
}
.slide-up-enter-active {
transform: translateY(0);
transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1);
}
8. DRY Pattern Summary
| Pattern | Implementation | Reused In |
|---|---|---|
| Page header with responsive actions | shared/_page_header.html.erb |
All feature index pages |
| Table with horizontal scroll | shared/_table_scroll_wrapper.html.erb |
Accounts, Investments, Analytics, Balance Registries, Construction, Leaderboard, Planning |
| Bottom sheet (modal) | shared/_turbo_modal.html.erb |
All turbo frame modal forms |
| FAB (floating action button) | shared/_fab.html.erb + fab_controller.js |
Accounts, Dashboard, Expenses, Investments, Goals |
| Bottom nav item | bottom_nav_item helper method |
_footer_nav.html.erb |
| Filter pill row | shared/_filter_pills.html.erb (new partial) |
Expenses, Investments, Habits, Analytics |
| Scrollable filter row | .scrollbar-none overflow-x-auto utility |
Same as above |
9. Files Changed — Complete Reference
| File | Type | Change |
|---|---|---|
app/views/layouts/application.html.erb |
Edit | pb-24 lg:pb-6 on main |
app/views/shared/_footer_nav.html.erb |
Rewrite | Full bottom nav with More drawer |
app/views/shared/_turbo_modal.html.erb |
Edit | Bottom sheet on mobile |
app/views/shared/_page_header.html.erb |
Edit | Stack layout + mobile_actions |
app/views/shared/_table_scroll_wrapper.html.erb |
New | Reusable table scroll wrapper |
app/views/shared/_fab.html.erb |
New | Floating action button |
app/views/shared/_filter_pills.html.erb |
New | Horizontal scrollable filter pills |
app/javascript/controllers/bottom_nav_controller.js |
New | "More" drawer toggle |
app/javascript/controllers/fab_controller.js |
New | Speed-dial FAB toggle |
app/javascript/controllers/index.js |
Edit | Register new controllers |
app/assets/stylesheets/application.css |
Edit | pb-safe, scrollbar-none, slide-up-* |
app/helpers/application_helper.rb |
Edit | Add bottom_nav_item helper |
app/views/organizations/dashboard/index.html.erb |
Edit | Remove min-w, wrap KPI table, use FAB |
app/views/organizations/accounts/index.html.erb |
Edit | Table wrapper, FAB, header stack |
app/views/organizations/expenses/index.html.erb |
Edit | Filter pills, summary grid, FAB |
app/views/organizations/investments/index.html.erb |
Edit | Filter pills, table wrapper, FAB |
app/views/organizations/goals/index.html.erb |
Edit | Chart container overflow |
app/views/organizations/analytics/index.html.erb |
Edit | Table wrappers, chart heights |
app/views/organizations/habits/index.html.erb |
Edit | Touch target sizes |
app/views/organizations/sports/index.html.erb |
Edit | Table wrapper |
app/views/organizations/notes/index.html.erb |
Edit | Grid layout |
app/views/organizations/gamification/leaderboard.html.erb |
Edit | Table wrapper |
app/views/organizations/planning/index.html.erb |
Edit | Table wrapper, simulator layout |
app/views/organizations/simulator/index.html.erb |
Edit | Full-width inputs, chart height |
app/views/organizations/construction_projects/show.html.erb |
Edit | Table wrapper |
app/views/organizations/balance_registries/index.html.erb |
Edit | Table wrapper |
app/views/landing_page/index.html.erb |
Edit | Hero size, pricing grid, feature flex |
app/views/static/pricing.html.erb |
Edit | Cards grid responsive |
app/views/devise/sessions/new.html.erb |
Edit | Verify padding |
app/views/devise/registrations/new.html.erb |
Edit | Verify padding |
10. Non-Goals (Out of Scope)
- Rearchitecting Stimulus controllers for touch-drag reordering (future enhancement)
- PWA offline improvements (separate concern)
- Admin views — staff-only tool, desktop-first is acceptable, excluded from this plan
- Adding swipe gestures to modals (can follow this plan)
- Changing the overall design language or colour palette
May the Force of clean, responsive HTML be with you, always.