Skip to content

fix(web): make every table readable on mobile#39

Merged
jeffgicharu merged 1 commit into
mainfrom
fix/mobile-tables
May 19, 2026
Merged

fix(web): make every table readable on mobile#39
jeffgicharu merged 1 commit into
mainfrom
fix/mobile-tables

Conversation

@jeffgicharu
Copy link
Copy Markdown
Owner

Problem

On a phone (~375px wide), almost every table in the app was unusable. Tables hid columns below the sm breakpoint with hidden sm:table-cell, leaving only the first ~2 columns visible and no way to scroll to the rest — e.g. the admin Invoices list showed only the invoice number and status, with the amount completely gone. A few contractor‑detail tabs (Invoices/Engagements) instead overflowed and clipped their right‑most columns.

The desktop layout was fine; this is a mobile‑only rendering bug.

Fix

Added shared responsive‑table primitives in components/ui/responsive-table.tsx (MobileCard, MobileCardList, MobileCardRow, TableScroll) and applied them consistently. The desktop <table> markup is untouched — it is simply hidden below sm, where a mobile rendering takes over.

Pattern chosen per table

Card‑stack (below sm) — for every entity‑list table. Each row becomes a card: the most important field is the title, the status badge sits opposite it, and the remaining fields are label/value lines so nothing is lost. Applied to:

  • Admin: Contractors, Invoices, Document Vault, 1099 Readiness, Offboarding, Audit Log (tap a card to expand the change diff)
  • Contractor detail tabs: Engagements, Invoices, Documents, Time Entries
  • Portal: Invoices, Payments, Documents, Time Entries, and the dashboard "Recent Invoices" widget (rendered as a lighter divided list to suit the compact widget context)

Horizontal scroll with a right‑edge fade affordance — for the invoice line‑item tables (admin + portal). These are dense, equal‑weight numeric columns (Description / Qty / Unit Price / Amount) that read better as a real table than as cards, so they keep the table and scroll sideways with a visible fade hinting there is more.

No data, copy, or behaviour changed. Desktop (≥sm) is byte‑for‑byte the same table as before.

Verification

Built, type‑checked, linted, unit tests (34) green. Verified against a real seeded DB at 375×812 (cards / scroll), 1024×768 (table, unchanged) and 1440×900 (table, unchanged) — desktop and tablet do not regress; the card stack only appears below 640px.

Before → After @ 375px

Page Before After
Admin · Contractors
Admin · Invoices
Admin · Document Vault
Admin · 1099 Readiness
Admin · Offboarding
Admin · Audit Log
Contractor detail · Invoices tab
Portal · Invoices
Portal · Payments
Portal · Invoice detail (line items, scrolled)

No desktop/tablet regression

Contractors @ 1440 Invoices @ 1440 Contractors @ 1024

Tables across the admin app and contractor portal dropped columns below
the sm breakpoint via hidden sm:table-cell, leaving only two columns
visible on a phone with no way to reach the rest; a few detail-tab
tables instead overflowed and clipped their right-most columns.

Add shared responsive-table primitives (MobileCard / MobileCardList /
MobileCardRow / TableScroll). Below sm, entity-list tables now render as
a stack of cards showing every field; the desktop table is unchanged and
simply hidden below sm. Invoice line-item tables keep a real table but
scroll horizontally with a right-edge fade affordance.

Covers contractors, invoices, documents, 1099 readiness, offboarding,
audit log, the contractor-detail engagement/invoice/document/time-entry
tabs, and the portal invoice/payment/document/time-entry/dashboard
views.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a set of responsive table components—MobileCardList, MobileCard, MobileCardRow, and TableScroll—to enhance the mobile user interface across multiple pages. Existing tables are now conditionally hidden on small screens in favor of a card-based layout or wrapped in a scrolling container. Review feedback suggests several improvements: restricting hover effects to interactive cards, using the formatCurrency utility for consistent formatting, replacing inline font styles with Tailwind's font-mono class, and correcting the indentation of table elements within the new scroll wrapper.

children?: ReactNode;
}) {
const inner = (
<div className="rounded-xl border border-slate-200 bg-white p-4 transition-colors hover:bg-slate-50">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The hover background color should only be applied if the card is interactive (i.e., when href or onClick is provided). Currently, it shows a hover state even for static cards, which can be misleading for users.

    <div className={`rounded-xl border border-slate-200 bg-white p-4 transition-colors ${href || onClick ? "hover:bg-slate-50" : ""}`}>

Comment on lines +265 to +268
$
{inv.totalAmount.toLocaleString('en-US', {
minimumFractionDigits: 2,
})}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency and to avoid manual string concatenation, use the shared formatCurrency utility here. Note that you will need to import it from @/lib/format.

Suggested change
$
{inv.totalAmount.toLocaleString('en-US', {
minimumFractionDigits: 2,
})}
{formatCurrency(inv.totalAmount)}

Comment on lines +244 to +245
className="text-sm font-medium text-brand-600"
style={{ fontFamily: 'JetBrains Mono, monospace' }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using inline styles for font families. Use the Tailwind font-mono class instead, which is already used elsewhere in the project.

                    <span className="font-mono text-sm font-medium text-brand-600">

Comment on lines +249 to 250
<TableScroll>
<table className="w-full border-separate border-spacing-0">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The <table> element should be indented relative to its new parent <TableScroll> to maintain code readability.

        <TableScroll>
          <table className="w-full border-separate border-spacing-0">

@jeffgicharu jeffgicharu merged commit 22b551b into main May 19, 2026
19 checks passed
@jeffgicharu jeffgicharu deleted the fix/mobile-tables branch May 19, 2026 16:26
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