Skip to content

Fix product-card overflow and pager wrapping; align Playwright image pin - #62

Merged
AviBackToBlack merged 3 commits into
mainfrom
fix/card-pager-layout
Aug 8, 2026
Merged

AviBackToBlack merged 3 commits into
mainfrom
fix/card-pager-layout

Conversation

@AviBackToBlack

@AviBackToBlack AviBackToBlack commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes the two reported layout bugs, plus the pre-existing breakage that had to be cleared to verify them.

Card price-pill overflow. .product-info could not shrink — .availability was white-space: nowrap and .price is flex: none — so on mobile columns (~165–195px of inner width) "While Stock Lasts" plus a 199.99 pill needed more space than the card had, and the pill rendered outside the card's right edge. Below 720px the row now stacks (label above pill); everywhere else the label wraps instead of overflowing.

Pager wrapping. The pager did not wrap because it was too wide, it wrapped because its width changed: pageWindow() returned 3 items on page 1, 4 on page 2, 5 on page 3 and 7 mid-range, so the control resized on every page change and the widest case overflowed, dropping Next onto a second line. It now always emits exactly slots items (7 desktop / 5 mobile via matchMedia, mirroring MOBILE_BREAKPOINT), ellipses occupy the same 38px box as a page button, and mobile compacts the buttons with icon-only Prev/Next under 400px.

Bonus bug found while verifying: hovering the current page button made its label invisible — .pagination button:hover (specificity 0,2,1) outranks .pagination .active-page (0,2,0), repainting the background light while the text stayed near-white.

Unblocking main (second commit). tests/e2e pinned @playwright/test@1.62.0 while .devcontainer/Dockerfile still pinned v1.61.1-noble, so every e2e test failed to launch a browser and CI was red. Bumping the image surfaced two WebKit 26 changes, both root-caused rather than papered over:

  • Registering a service worker while page.route() interception is active wedges the page — waitForTimeout still resolves but every protocol call times out. AlertsModal registers /sw.js on open, which hung all four modal specs on webkit only. routeOffers() now aborts **/sw.js; the SPA specs don't exercise the worker and pwa*.spec.ts, which do, never mock routes.
  • PushManager/Notification are now exposed headlessly, so isPushSupported() is true and the modal renders its .push-row (+84px, exactly the snapshot delta). Genuine capability change, so alerts-modal-webkit.png is re-recorded.

Also anchors the root .gitignore's Python build dirs (/lib/, /lib64/): unanchored lib/ matched frontend/src/lib/ at any depth, and git only kept the app source because frontend/.gitignore re-includes it — tooling that reads only the root ignore file treats the whole app tree as ignored.

Test plan

  • make test passes locally in the rebuilt image (unit, installer, e2e 89/89 across chromium/firefox/webkit/chromium-push, load, security)
  • Manually exercised the change: captured the grid and the pager at 320 / 375 / 430 / 1280px across pages 1–4. Card label and pill now sit inside the card with consistent alignment; pager geometry is pixel-identical between pages and stays on one line at every width.
  • New/updated tests: pageWindow now asserts a constant slot count, ordering, uniqueness and clamping over every page of several ranges; the e2e pager spec asserts the slot count and pager height do not change while paging.

Notes for the reviewer

  • Deliberate UX trade-off: on phones the pager shows < 1 … 4 … 23 > — the ±1 neighbours are dropped, because 7 slots cannot fit one line at 320px even compacted. Paging stays one tap via Prev/Next, and arrow-key paging is unaffected.
  • Prev/Next gained aria-label="Previous page" / "Next page" so the accessible names survive hiding the words under 400px; the one e2e selector that used name: 'Next' was updated.
  • Constant slot count makes bridging a one-page gap free (same slot either way), so that existing behaviour is kept rather than regressed into an ellipsis.
  • .npmrc (legacy-peer-deps=true, added on main just before this branch) is validated here — npm ci works in the container again. Worth remembering it is repo-wide and will also silence real peer conflicts, e.g. the vite/vitest/vite-plugin-svelte chain documented in CLAUDE.md.
  • Gotchas from this investigation are recorded in CLAUDE.md so the next agent does not re-derive them.

Generated with Devin


Open in Devin Review

AviBackToBlack and others added 2 commits August 8, 2026 15:40
The product card's info row could not shrink: .availability was
white-space: nowrap and .price is flex: none, so on mobile columns
(~165-195px of inner width) 'While Stock Lasts' plus a 199.99 pill
needed more room than the card had and the pill rendered outside the
card's right edge. Below 720px the row now stacks (label above pill);
elsewhere the label wraps instead of overflowing.

The pager did not wrap because it was too wide — it wrapped because its
width changed. pageWindow() returned 3 items on page 1, 4 on page 2, 5
on page 3 and 7 mid-range, so the control resized on every page change
and the widest case overflowed, dropping Next onto a second line. It now
always emits exactly slots items (7 desktop, 5 mobile via matchMedia
mirroring MOBILE_BREAKPOINT), ellipses occupy the same 38px box as a page
button, and mobile compacts the buttons with icon-only Prev/Next under
400px. Constant slot count also makes bridging a one-page gap free, so
that behaviour is kept.

Also fixes an unrelated hover bug spotted while verifying: the current
page's label went invisible on hover, because .pagination button:hover
(0,2,1) outranks .pagination .active-page (0,2,0) and repainted the
background light while the text stayed near-white.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
tests/e2e pinned @playwright/test 1.62.0 while .devcontainer/Dockerfile
still pinned the v1.61.1-noble image, so every e2e test failed to launch
a browser ('Please update docker image as well') and main was red. Bump
the image and the version strings that document it.

That surfaced two WebKit 26 behaviour changes:

- Registering a service worker while page.route() interception is active
  wedges the page: waitForTimeout still resolves but every protocol call
  (locator.count, evaluate, screenshot) times out. AlertsModal registers
  /sw.js on open, which hung all four modal specs on webkit only. Confirmed
  by aborting /sw.js — the modal then opens and the page stays responsive.
  routeOffers() now aborts it; the SPA specs never exercise the worker and
  pwa*.spec.ts, which do, never mock routes.
- PushManager/Notification are now exposed headlessly, so isPushSupported()
  is true and the modal renders its .push-row (+84px, exactly the snapshot
  delta). Genuine capability change, so alerts-modal-webkit.png is
  re-recorded.

Also anchor the root .gitignore's Python build dirs (/lib/, /lib64/):
unanchored 'lib/' matched frontend/src/lib/ at any depth, and git only kept
the app source because frontend/.gitignore re-includes it — tooling that
reads just the root file treats the whole tree as ignored.

Verified in the rebuilt image: make test green (unit, installer, e2e 89/89
across all engines, load, security).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 4 potential issues.

Open in Devin Review

Comment thread frontend/src/lib/logic/pager.ts
Comment thread tests/e2e/fixtures/offers.ts
Comment thread frontend/src/lib/logic/pager.ts
Comment thread frontend/src/lib/components/Card.svelte
Devin Review asked whether the desktop baselines were genuinely unaffected
by dropping white-space: nowrap on .availability, or whether the 0.02
maxDiffPixelRatio was hiding a change. Measured it: renders are byte-stable
across repeat runs (control passes), and with the nowrap version as a
zero-tolerance baseline the same browser produces a page 1px taller
(1280x1398 -> 1280x1399, 6706 differing pixels, ratio 0.01). So the grid
snapshots were passing on tolerance, not on being identical — exactly the
masking the review flagged.

Re-record all visual baselines with the 1.62.0 browsers this PR pins, so
they reflect the actual rendering instead of being stale-but-tolerated.
harness-smoke's static-fixture snapshots are unchanged.

Also document two implicit things the review called out: the mobile slot
count encodes radius = (slots - 5) / 2, so 5 slots deliberately means no
±1 neighbours and 7 is the minimum to get them back; and routeOffers()
aborting /sw.js applies to every caller, so a future offline assertion in
those specs would silently test a no-SW app.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@AviBackToBlack
AviBackToBlack merged commit 05f8cef into main Aug 8, 2026
8 of 9 checks passed
@AviBackToBlack
AviBackToBlack deleted the fix/card-pager-layout branch August 8, 2026 19:39
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