Skip to content

feat(mobile): make tables, forms and touch targets work at 375px - #7

Merged
Asapteejo merged 3 commits into
mainfrom
feat/mobile-first-layout
Sep 17, 2026
Merged

Asapteejo merged 3 commits into
mainfrom
feat/mobile-first-layout

Conversation

@Asapteejo

Copy link
Copy Markdown
Owner

Audited at 375px with touch emulation on the live tenant site and on admin with a real session, measuring element geometry rather than eyeballing screenshots. Every number below is measured before and after.

Tables — the worst offender

DataTable only wrapped the table in overflow-x-auto, so at 375px:

  • /admin/payments rendered 973px wide inside a 333px column — three screens of sideways scrolling
  • /admin/invoices 775px
  • /admin Deal Board was worse and isn't a table at all: a hardcoded min-w-[960px] grid

Below md, each row now renders as a card (first column as heading, the rest as label/value pairs, row actions preserved); the table returns at md and up. It lands once in the shared component, so all nine DataTable screens plus SimpleDataTable/DataTableCard benefit. The Deal Board stacks its stage columns on phones.

Not covered by this PR: 11 files still render raw <table> outside the shared primitives (admin analytics, benchmarks, listings/marketplace, team/commission-rules, incident/support management, development-calculator, 4 superadmin screens). They still side-scroll.

iOS zoom on every form

Input, Select and Textarea used 14px text. iOS Safari zooms the whole page when a focused field is under 16px, which is why forms "jumped" on a phone. They are now 16px below sm, keeping the denser 14px above it. The same fix went to the 9 inline input styles that bypassed the primitives (also 38px tall → 44px), and phone fields in the front-desk/quick-create forms gained type="tel".

Touch targets (44px minimum)

Measured offenders, all now 44px on phones and unchanged from sm up: Button size="sm" (36px, the admin row-action size), dialog close (36px), DataTable sort controls (16px tall), pagination (36px), theme toggle (36px), announcement dismiss (28px), quick actions (40px), footer links (20px), property filter checkboxes (13px).

Content priority

Admin pages stacked ~1600px of chrome above the data: the ambient clock is hidden on phones and page descriptions clamp to two lines. /properties buried every listing under six stacked filters — they now collapse behind a toggle, implemented with CSS only (no JS, still server-rendered) and forced open from md up.

Dialogs

The bottom-sheet behaviour under sm was already correct. Added safe-area padding so actions clear the home indicator, and footer buttons now stack full-width on phones.

Measured result

Screen Small targets Side-scroll iOS-zoom fields
/admin 31 → 11 960px grid → none —
/admin/payments 20 → 6 973px table → none 6 → 0
/admin/invoices 26 → 5 775px table → none 6 → 0
tenant / 9 → 4 none 3 → 0
tenant /contact 8 → 4 11px page overflow → 0 4 → 0

Remaining sub-44px items are per-screen links and chips (e.g. "Create deal", "Add line item"), not shared primitives — a follow-up.

Design tokens and dark mode are untouched: this is layout and interaction only. npm run check green (533 tests, typecheck, lint, build).

🤖 Generated with Claude Code

Measured at 375px with touch emulation, before and after.

Tables (the worst offender). DataTable only wrapped the table in
overflow-x-auto, so /admin/payments rendered 973px wide inside a 333px
column and /admin/invoices 775px — three screens of sideways scrolling.
Below `md` each row is now a card: first column as the heading, the rest
as label/value pairs, actions preserved. The table returns at `md`. This
lands once in the shared component, so all nine DataTable screens plus
SimpleDataTable/DataTableCard get it. The Deal Board was worse — a
hardcoded min-w-[960px] grid, not a table at all — and now stacks its
stage columns below `md`.

iOS zoom. Input/Select/Textarea were 14px, and iOS Safari zooms the page
whenever a focused field is under 16px — every form on every phone. They
are 16px below `sm` and keep 14px above it. Same for the 9 inline input
styles that bypassed the primitives, which were also 38px tall (now 44).
Phone fields in the front-desk and quick-create forms gained type="tel"
so they raise the right keyboard.

Touch targets (44px minimum). Button size="sm" was 36px and is the admin
row-action size; dialog close 36px; DataTable sort controls 16px tall;
pagination 36px; theme toggle 36px; announcement dismiss 28px; quick
actions 40px; footer links 20px; property filter checkboxes 13px. All are
44px on phones and keep their denser desktop sizing from `sm` up.

Content priority. Admin pages put ~1600px of chrome above the data; the
ambient clock is hidden on phones and page descriptions clamp to two
lines. /properties buried every listing under six stacked filters, which
now collapse behind a toggle (CSS-only, still server-rendered, always
open from `md` up).

Dialogs. The bottom-sheet behaviour was already right; it gained
safe-area padding so actions clear the home indicator, and its footer
buttons stack full-width on phones.

Measured result (small targets per screen, iOS-zoom fields, side-scroll):
  /admin            31 -> 11, 960px grid gone
  /admin/payments   20 -> 6,  973px table gone, 6 zoom fields -> 0
  /admin/invoices   26 -> 5,  775px table gone, 6 zoom fields -> 0
  tenant /           9 -> 4,  3 zoom fields -> 0
  tenant /contact    8 -> 4,  11px horizontal overflow -> 0

Design tokens and dark mode are untouched; this is layout and interaction
only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
estate-os Ready Ready Preview Sep 17, 2026 8:19am UTC

Asapteejo and others added 2 commits September 17, 2026 08:57
health.spec.ts uses the API request context, which runs in Node and so
does not get Chromium's --host-resolver-rules mapping for the
<tenant>.localhost host. On any platform whose resolver does not handle
*.localhost (Windows) the three health tests failed with ENOTFOUND while
the browser-driven specs passed.

The health endpoints are not tenant-specific, so a local run talks to
loopback directly; an E2E_BASE_URL pointing at a deployment is used
unchanged, which is how this spec is meant to gate a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Asapteejo
Asapteejo merged commit 4f539c7 into main Sep 17, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — f8ebdc20 Deployed Sep 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant