Skip to content

Mobile: scroll-shadow cue on horizontally-scrolling data tables (#150) - #151

Merged
mforce merged 2 commits into
mainfrom
feat/table-scroll-cue
Jul 22, 2026
Merged

mforce merged 2 commits into
mainfrom
feat/table-scroll-cue

Conversation

@mforce

@mforce mforce commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Closes #150.

Problem

Below 900px table.data becomes overflow-x: auto. Mobile scrollbars are overlay-only — they appear during a scroll and vanish at rest — so a wide table gives no sign the last column is clipped. Reported from the Sales list at 412px: the Customer column cut mid-word ("Hotel Paraí…") with no cue. Affects every data table (Sales, History, Audit, Stock, Reports).

Fix

Lea Verou's pure-CSS scroll-shadow, dropped into the shared table.data rule so it covers every table at once — no JS, no per-table markup:

  • two cover layers (--surface → transparent, background-attachment: local) ride the content and mask the shadow at whichever edge you've reached;
  • two shadow layers (--scroll-cue → transparent, background-attachment: scroll) sit at the element edges and show through wherever content still overflows.

Net: right fade while there's more right, left fade once scrolled, neither at rest when the table fits. --scroll-cue is a theme token (light + both dark blocks); the cover uses --surface, so both themes work. Static gradient → nothing to gate on prefers-reduced-motion.

Verification

Headless Chromium at 412px, light + dark. A debug render (covers→lime, shadows→red) confirmed the geometry: at rest the left cover masks the left shadow and the right shadow shows; mid-scroll both edges show; a table that fits shows neither. Real render at the shipped alpha shows a soft right-edge fade past the last visible column.

state cue
wide, at rest right fade only
wide, scrolled left fade (+ right while still more)
fits viewport none

Tests

373 pass (+1). New StockPage guard asserts the table.data hook the CSS keys off still renders — the class is the whole contract, so a refactor dropping it would silently kill the affordance. jsdom can't render the gradient (visual, browser-verified above). Coverage above floor (90.64 / 84.46 / 71.07). tsc + vite build clean.

Docs

No new user-facing term (a visual affordance, not a named concept), so no GLOSSARY / Help / glossary-table change — consistent with the issue's checklist.

Deferred (own slice): stacking ID-heavy rows into cards to remove horizontal scroll entirely; the scroll-shadow is the cheap fix that removes the "is there more?" ambiguity now.

Part of epic #14 (Phase 1.1).

mforce added 2 commits July 22, 2026 16:10
Below 900px table.data becomes an overflow-x scroller. Mobile scrollbars
are overlay-only (vanish at rest), so a wide table gave no sign the last
column was clipped — reported from the Sales list where the Customer
column was cut mid-word.

Add the Lea Verou pure-CSS scroll-shadow to the shared table.data rule:
two surface->transparent cover layers pinned to the content (local) mask
the shadow at whichever edge is reached; two --scroll-cue radial layers
pinned to the scroller (scroll) show through wherever content overflows.
Result: a right fade while there is more right, a left fade once scrolled,
neither at rest with no overflow. No JS, no per-table markup, so every
data table (Sales, History, Audit, Stock, Reports) gets it at once.

--scroll-cue is a theme token (light + both dark blocks); the cover uses
--surface, so it works in both themes. Static gradient, so nothing to
gate on prefers-reduced-motion.

Guarded by a StockPage test asserting the table.data hook the CSS keys
off still renders.
Two fixes from review + phone testing of the History table:

- Dark mode showed no cue: a black shadow (rgba(0,0,0,.5)) is invisible on
  the near-black table surface. Switch --scroll-cue in both dark blocks to a
  light glow (rgba(255,255,255,.16)) so the overflow edge reads as 'more here'.

- The cue faded out toward the top and bottom rows: the shadow layers were
  radial (Verou's original), which on a tall table (History runs a dozen rows)
  leaves the first and last rows uncued. Switch them to linear gradients for a
  uniform bar down the whole edge (pi review). Also widen the cover's solid
  stop 35% -> 40% so it fully spans the 0.85rem cue with nothing peeking at the
  rested edge.

Re-verified at 412px, light and dark, on a 12-row table: uniform edge cue top
to bottom, none when the table fits.
@mforce

mforce commented Jul 22, 2026

Copy link
Copy Markdown
Owner Author

Review round complete — codex + 2 Claude agents + pi

codex — no findings, approve. All four background layers valid; local covers / scroll cues behave as intended; --scroll-cue covers light/explicit-dark/OS-dark; no regression to hover rows, collapsed borders, or desktop; RTL cue follows real scroll geometry; test stable.

Claude agent 1 (feature-dev) — no high-confidence defects. Confirmed shorthand ordering, the local/scroll cover-masks-cue behavior on the self-scrolling table.data, token presence in all three blocks, no cascade conflict with tr:hover (targets <tr>, not <table>), and that the guard hits exactly one table.data (lots/movement tables only mount on drill-down).

Claude agent 2 (cavecrew) — no issues. Same conclusions.

pi (deepseek-v4-flash, single-pass) — no functional defect; two polish items adopted:

  1. Cue layers were radial-gradient → the cue faded toward the top/bottom rows, uneven on a tall table. Switched to linear-gradient for a uniform bar down the whole edge.
  2. Cover solid stop 35% → 40% so it fully spans the 0.85rem cue (kills the ~0.02α sliver at the rested edge).

Extra fix from phone testing (not a reviewer finding)

Testing the dark History table on a real phone showed no cue at all: a dark shadow (rgba(0,0,0,.5)) is invisible on the near-black surface. Switched --scroll-cue in both dark blocks to a light glow (rgba(255,255,255,.16)). Re-verified at 412px on a 12-row table, light + dark: uniform edge cue top-to-bottom, none when the table fits.

373 tests pass, tsc + vite build clean. CI re-running on the latest push.

@mforce
mforce merged commit 06cdfa8 into main Jul 22, 2026
3 checks passed
@mforce
mforce deleted the feat/table-scroll-cue branch July 22, 2026 23:23
@mforce mforce mentioned this pull request Jul 22, 2026
90 tasks done
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.

Mobile data tables scroll sideways with no cue — the last column is silently clipped

1 participant