Skip to content

Commit 0b499ab

Browse files
committed
Grow the up-next preview to a five-deep vertical stack on desktop
Phones keep the two-tile strip. On wide screens (the existing 900px breakpoint) the strip now fills five tiles bottom-up via column-reverse: the very next image sits at the bottom, the farthest-out on top. Tiles cap at 12dvh so all five fit inside the stage on short windows, and the JS fill count follows the same media query live on resize. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EHGNXqgRVUa2qVY6SYofg9
1 parent 056c86c commit 0b499ab

4 files changed

Lines changed: 48 additions & 11 deletions

File tree

e2e/tests/triage.spec.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ test.beforeEach(async ({ page }) => {
3131
page.locator("#cards .card").count()).toBe(3);
3232
});
3333

34+
// These two run first: later tests decide items (trash/keep), shrinking the
35+
// shared seeded library below the 4 undecided-behind-the-top-card these counts
36+
// assume.
37+
test("desktop stacks the up-next previews vertically, next at the bottom", async ({ page }) => {
38+
// 5 seeded items: one is the top card, so all 4 others fit in the desktop
39+
// preview stack (capacity 5).
40+
const tiles = page.locator("#upnext .upnext-tile:not(.hidden)");
41+
await expect(tiles).toHaveCount(4);
42+
// column-reverse is what puts the next-up image at the bottom and the
43+
// farthest-out on top.
44+
const dir = await page.locator("#upnext")
45+
.evaluate((el) => getComputedStyle(el).flexDirection);
46+
expect(dir).toBe("column-reverse");
47+
});
48+
49+
test("a phone-width viewport keeps the two-tile preview", async ({ page }) => {
50+
await page.setViewportSize({ width: 390, height: 844 });
51+
await expect(page.locator("#upnext .upnext-tile:not(.hidden)")).toHaveCount(2);
52+
});
53+
3454
test("keep advances the stack and undo brings the card back", async ({ page }) => {
3555
const first = await topCardId(page);
3656
expect(first).toBeTruthy();

frontend/app.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,13 @@ function topUpVisible() {
192192
const cardEls = new Map(); // item.id -> card element (reused across renders)
193193

194194
// --- "up next" preview -------------------------------------------------------
195-
// Two static tiles created once in index.html; JS only toggles classes and
195+
// Five static tiles created once in index.html; JS only toggles classes and
196196
// swaps src on identity change, so the imgs are never recreated (no flicker).
197+
// Phones fill just the first two; wide screens fill all five as a vertical
198+
// stack. The breakpoint must match the .upnext media query in styles.css.
197199
const upnextTiles = [...document.querySelectorAll("#upnext .upnext-tile")];
200+
const upnextWide = window.matchMedia("(min-width: 900px)");
201+
upnextWide.addEventListener("change", () => updateUpNext());
198202

199203
// Tile resolution: 256 on phones, 512 once the CSS clamp grows tiles past
200204
// ~256 device px (iPad/desktop, retina).
@@ -205,8 +209,10 @@ function upnextRes() {
205209

206210
function updateUpNext() {
207211
// The queue front is state.visible (top = index 0), refilled from
208-
// state.buffer, so the two items after the top card are:
209-
const upcoming = [...state.visible.slice(1, VISIBLE), ...state.buffer].slice(0, 2);
212+
// state.buffer, so the items after the top card are:
213+
const shown = upnextWide.matches ? upnextTiles.length : 2;
214+
const upcoming =
215+
[...state.visible.slice(1, VISIBLE), ...state.buffer].slice(0, shown);
210216
const size = upnextRes();
211217
upnextTiles.forEach((tile, i) => {
212218
const item = upcoming[i];

frontend/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
<div id="upnext" class="upnext hidden" aria-hidden="true">
5656
<div class="upnext-tile hidden"><img alt="" decoding="async"><span class="upnext-copies hidden"></span></div>
5757
<div class="upnext-tile hidden"><img alt="" decoding="async"><span class="upnext-copies hidden"></span></div>
58+
<div class="upnext-tile hidden"><img alt="" decoding="async"><span class="upnext-copies hidden"></span></div>
59+
<div class="upnext-tile hidden"><img alt="" decoding="async"><span class="upnext-copies hidden"></span></div>
60+
<div class="upnext-tile hidden"><img alt="" decoding="async"><span class="upnext-copies hidden"></span></div>
5861
</div>
5962
</main>
6063

frontend/styles.css

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,20 +167,28 @@ body {
167167
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
168168
}
169169

170-
/* "Up next" preview: the next two queue items, pinned bottom-right of the
171-
stage. Tile size clamps across three anchors: 64px (phones) -> 9vw
172-
(scales with viewport) -> 128px (iPad/desktop). z105: above the cards
173-
(z100..98, set in setDepth) but below the backdrop (z110) so an open
174-
sheet dims it. pointer-events: none so it can never eat a swipe that
175-
starts over the card's corner. */
170+
/* "Up next" preview, pinned bottom-right of the stage: the next two queue
171+
items on phones, the next five on wide screens. Tile size clamps across
172+
three anchors: 64px (phones) -> 9vw (scales with viewport) -> 128px
173+
(iPad/desktop). z105: above the cards (z100..98, set in setDepth) but
174+
below the backdrop (z110) so an open sheet dims it. pointer-events: none
175+
so it can never eat a swipe that starts over the card's corner. */
176176
.upnext {
177177
--upnext-size: clamp(64px, 9vw, 128px);
178178
position: absolute; right: 10px; bottom: 10px;
179179
display: flex; flex-direction: column; gap: 8px;
180180
z-index: 105; pointer-events: none;
181181
}
182-
/* Wide screens: sit the tiles side-by-side at the max clamp size. */
183-
@media (min-width: 900px) { .upnext { flex-direction: row; } }
182+
/* Wide screens: a vertical stack of up to five, filled bottom-up — the very
183+
next image sits at the bottom, the farthest-out on top. The 12dvh cap
184+
keeps all five inside the stage on short windows; the JS fill count keys
185+
off this same 900px breakpoint. */
186+
@media (min-width: 900px) {
187+
.upnext {
188+
flex-direction: column-reverse;
189+
--upnext-size: min(128px, 12dvh);
190+
}
191+
}
184192
.upnext-tile {
185193
position: relative;
186194
width: var(--upnext-size); height: var(--upnext-size);

0 commit comments

Comments
 (0)