fix(responsive): stop desktop tables and mobile rows from silently clipping content

Red-team pass on the redesigned Client 360 / Tickets / Dashboard / Modules screens found
real overlap/clipping bugs, all fixed:

- Client header still broke at 768px (tablet): the button cluster only stacked below the
  name under 640px, so at 768 the name got squeezed to ~38px and force-wrapped mid-word.
  Bumped the stacking breakpoint to 860px.
- Per-module Documents rows and the Payments Outstanding-panel rows were plain nowrap flex
  rows with no scroll; at 375px the amount/status badge silently clipped past the card
  edge with no way to reach them. Both now wrap.
- Biggest one: every DataTable silently lost its rightmost columns at desktop width
  (>=901px) — table.wf's display:block/table toggle assumed overflow-x:auto still worked
  once display flipped to table, but Chromium computes overflow-x as visible on a
  display:table box, so wide tables (Tickets' 11 columns) overflowed the viewport with no
  scrollbar. Fixed at the root: DataTable now wraps its <table> in a .wf-table-wrap scroll
  container instead of relying on the table element to scroll itself.

Verified against screenshots at 375/768/1440, light+dark, before and after; typecheck and
the full test suite (498 passed) stay clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main
Thomas Joise 24 hours ago
parent b146de990f
commit 0bcbf8d410

@ -270,10 +270,14 @@ button.dash-row:hover { background: var(--accent-soft); }
.hq-content { padding: 8px 14px 20px; }
.wf-page { padding: 14px 2px 28px; }
}
/* Tables scroll inside their page rather than breaking the layout. */
/* Tables scroll inside their own box rather than the page (rule 8 no silent caps).
This needs a wrapping div: a `<table>` (display: table) doesn't reliably become its
own horizontal-scroll container from `overflow-x: auto` in Chromium, so at desktop
widths a wide table (e.g. Tickets' 11 columns) was overflowing past the viewport and
getting silently clipped by `.hq-content`'s overflow-x: hidden below, unreachable at
any width. The wrapper scrolls at every width now, not just <901px. */
.hq-content { overflow-x: hidden; }
table.wf { display: block; overflow-x: auto; }
@media (min-width: 901px) { table.wf { display: table; } }
.wf-table-wrap { overflow-x: auto; }
/* Relationship-pulse ribbon (Client 360 overview). */
.pulse {
@ -515,7 +519,7 @@ table.wf tbody tr[role='button']:focus-visible td:first-child { box-shadow: inse
/* outstanding panel */
.cdr-out { border: 1px solid color-mix(in srgb, var(--warn) 30%, var(--border)); border-radius: var(--radius); overflow: hidden; }
.cdr-out .oh { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border); }
.cdr-out .orow { display: flex; align-items: center; gap: 12px; padding: 9px 14px; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.cdr-out .orow { display: flex; align-items: center; flex-wrap: wrap; row-gap: 4px; gap: 12px; padding: 9px 14px; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.cdr-out .orow:last-child { border-bottom: none; }
.cdr-out .orow .due { margin-left: auto; font-family: var(--mono); font-weight: 600; }
/* document-type pill */

@ -1557,7 +1557,7 @@ function ModuleDocuments(props: { cm: ClientModule; docs: Doc[]; moduleCode: str
{mine.length === 0 ? <div style={{ color: 'var(--text-dim)', fontSize: 12.5 }}>No documents for this module yet.</div>
: mine.map((d) => (
<div key={d.id} onClick={() => nav(`/documents/${d.id}`)}
style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '7px 0', borderTop: '1px solid var(--border)', cursor: 'pointer', fontSize: 12.5 }}>
style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', rowGap: 4, gap: 10, padding: '7px 0', borderTop: '1px solid var(--border)', cursor: 'pointer', fontSize: 12.5 }}>
<span className="mono" style={{ minWidth: 74 }}>{d.docNo ?? 'draft'}</span>
<span className="doc-pill">{docLabel(d)}</span>
<span className="mono" style={{ color: 'var(--text-dim)' }}>{d.docDate}</span>

@ -134,6 +134,7 @@ export function DataTable(props: {
}
return (
<div className="wf-table-wrap">
<table className="wf">
<thead>
<tr>
@ -180,6 +181,7 @@ export function DataTable(props: {
})}
</tbody>
</table>
</div>
)
}

@ -462,10 +462,12 @@ button.wf.pill { border-radius: 999px; padding: 8px 18px; }
.wf-formgrid .wide { grid-column: 1 / -1; }
@media (max-width: 560px) { .wf-formgrid { grid-template-columns: 1fr; } }
/* ================= mobile (phone) layout ================= */
@media (max-width: 640px) {
/* ================= mobile + tablet layout ================= */
@media (max-width: 860px) {
/* Record header: stack the action-button cluster below the avatar/name/meta instead of
crowding them on one row. Desktop layout (outside this query) is untouched. */
crowding them next to it and squeezing the name into a sliver (seen at ~768px with a
long client name + a full button cluster). Desktop layout (outside this query) is
untouched. */
.wf-rec { flex-wrap: wrap; }
.wf-rec-actions { width: 100%; }
}

Loading…
Cancel
Save