Float the phone's navigation: a tab bar, large titles and a column switcher #243

Merged
Grandiras merged 3 commits from ui-v2-phone into main 2026-10-03 02:20:57 +00:00
Owner

What

The phone navigation of UI language v2 (Figma, page Screens · Phone), below 600px. 600px and up is unchanged.

  • A floating tab bar. Home and Calendar sit in a pill of material (the current one a raised thumb), with Search as a round button beside it, inset from the edges. --phone-nav-h is still the one height pages subtract; it is now the 62px bar plus its float gap, once below and once above.
  • The account is on Home only. Home gets a large title (Home over today's date) with your face in a disc of material beside it. It opens the same account menu as before, now as a sheet over the tab bar.
  • A board's top (BoardPhoneBar) is a row of floating buttons: back as a pill reading "Home", Share, and the cog for its admins. Below them the board's name is a large title over its card count. Settings pages use the same bar, with the board's name in the pill and "Board settings" as the title. The page's own heading is hidden on the phone so the title is said once.
  • A column switcher in place of the pager dots. It is a segmented control (--fill-1 track, --bg-thumb + --shadow-sm thumb) showing each column's name and count. A tap pages the board to that column. The thumb follows the board's scroll, so a swipe slides it. When the columns overflow, the track scrolls and keeps the lit segment in view. While a card is dragged, each segment is still a drop target, as the dots were.
  • Sheets cover the tab bar, which steps aside under them (the draft's rule). The account and search sheets now slide up from the bottom edge. The account sheet's dim also covers the bar, and the face stays above the dim so a second tap still closes it. The card sheet is unchanged.

Why

The redesign is signed off; this is its phone part.

Choices worth a look

  • Share moved into the board's top bar. The account menu was the only place a phone could share a board. Now that the menu lives on Home, the board would have lost Share. The draft puts a "⋯" there; Share plus the cog keeps both actions without adding a new menu.
  • Home stays lit on a board. The draft's board frame lights no tab. An iOS tab stays selected over a page opened from it, and keeping it lit avoids churn.
  • Column headers stay on the phone, because the tag filter lives in them. Name and count therefore appear in both the segment and the header.
  • The face on Home is fixed, so it stays put while Home scrolls instead of scrolling away with the title. The header has to stay a real box, since the suite and the docs rely on it being the bar.
  • Reduced motion: the nav sheets appear without the slide, and a tap pages the board instantly.

Not in this PR

The two-detent card sheet, the large title collapsing into the nav bar on scroll, the calendar's large title, Home's boards as a grouped list, and the "Drop on a column" hint.

How it was checked

  • tools/verify.sh: build plus 584 unit tests, green.

  • The full Playwright suite (159 tests) ran on an isolated stack. 158 passed on the first run. The one failure, search.spec.js › search finds a card by what is written on it…, was a desktop composer race in which the description landed in the title; it is unrelated to the phone and passed on both reruns. mobile.spec.js now covers:

    • the floating bar: equal side insets, and the board ending as far above the bar as the bar floats off the edge
    • the two labelled tabs and the round Search
    • no face on a board
    • the board's top and the switcher: a tap pages, a swipe moves the thumb, and an overflowing track keeps the lit segment in view
    • the face on Home, level with the title, and its sheet covering the bar
    • a card held over a segment moving to that column (touch, over CDP)

    boards.spec.js, search.spec.js and board.spec.js follow the new shapes.

  • Screenshots from a dev app with the canvas seed:

    • 375, 390 and 412 wide, light and dark: Home, a board, a board with eight columns, the account sheet, search, a card, the calendar and settings, compared against the Figma frames.
    • 820 and 1440 wide: pixel-identical to main for Home, a board, settings and the calendar.

🤖 Generated with Claude Code

## What The phone navigation of UI language v2 (Figma, page *Screens · Phone*), below 600px. 600px and up is unchanged. - **A floating tab bar.** Home and Calendar sit in a pill of material (the current one a raised thumb), with Search as a round button beside it, inset from the edges. `--phone-nav-h` is still the one height pages subtract; it is now the 62px bar plus its float gap, once below and once above. - **The account is on Home only.** Home gets a large title (*Home* over today's date) with your face in a disc of material beside it. It opens the same account menu as before, now as a sheet over the tab bar. - **A board's top** (`BoardPhoneBar`) is a row of floating buttons: back as a pill reading "Home", Share, and the cog for its admins. Below them the board's name is a large title over its card count. Settings pages use the same bar, with the board's name in the pill and "Board settings" as the title. The page's own heading is hidden on the phone so the title is said once. - **A column switcher in place of the pager dots.** It is a segmented control (`--fill-1` track, `--bg-thumb` + `--shadow-sm` thumb) showing each column's name and count. A tap pages the board to that column. The thumb follows the board's scroll, so a swipe slides it. When the columns overflow, the track scrolls and keeps the lit segment in view. While a card is dragged, each segment is still a drop target, as the dots were. - **Sheets cover the tab bar**, which steps aside under them (the draft's rule). The account and search sheets now slide up from the bottom edge. The account sheet's dim also covers the bar, and the face stays above the dim so a second tap still closes it. The card sheet is unchanged. ## Why The redesign is signed off; this is its phone part. ## Choices worth a look - **Share moved into the board's top bar.** The account menu was the only place a phone could share a board. Now that the menu lives on Home, the board would have lost Share. The draft puts a "⋯" there; Share plus the cog keeps both actions without adding a new menu. - **Home stays lit on a board.** The draft's board frame lights no tab. An iOS tab stays selected over a page opened from it, and keeping it lit avoids churn. - **Column headers stay on the phone**, because the tag filter lives in them. Name and count therefore appear in both the segment and the header. - **The face on Home is fixed**, so it stays put while Home scrolls instead of scrolling away with the title. The header has to stay a real box, since the suite and the docs rely on it being the bar. - **Reduced motion:** the nav sheets appear without the slide, and a tap pages the board instantly. ## Not in this PR The two-detent card sheet, the large title collapsing into the nav bar on scroll, the calendar's large title, Home's boards as a grouped list, and the "Drop on a column" hint. ## How it was checked - `tools/verify.sh`: build plus 584 unit tests, green. - The full Playwright suite (159 tests) ran on an isolated stack. 158 passed on the first run. The one failure, `search.spec.js` › *search finds a card by what is written on it…*, was a desktop composer race in which the description landed in the title; it is unrelated to the phone and passed on both reruns. `mobile.spec.js` now covers: - the floating bar: equal side insets, and the board ending as far above the bar as the bar floats off the edge - the two labelled tabs and the round Search - no face on a board - the board's top and the switcher: a tap pages, a swipe moves the thumb, and an overflowing track keeps the lit segment in view - the face on Home, level with the title, and its sheet covering the bar - a card held over a segment moving to that column (touch, over CDP) `boards.spec.js`, `search.spec.js` and `board.spec.js` follow the new shapes. - Screenshots from a dev app with the canvas seed: - 375, 390 and 412 wide, light and dark: Home, a board, a board with eight columns, the account sheet, search, a card, the calendar and settings, compared against the Figma frames. - 820 and 1440 wide: pixel-identical to `main` for Home, a board, settings and the calendar. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Below 600px, as UI language v2 draws it. Home and Calendar sit in a floating
pill of material with Search as a round button beside it; the account is a
face beside Home's new large title, and its menu is a sheet over the bar. A
board's top is a row of floating buttons (back, Share, and the cog for its
admins) over its name as a large title, and a segmented control of its
columns replaces the pager dots: a tap pages there, the thumb follows a
swipe, and a card held over a segment moves to that column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Line Home's title up with the face, and give the touch test room under the new top
All checks were successful
CI / build (pull_request) Successful in 36s
fe1ca2b8bd
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-243/run-2026-10-03T01-50-36-791Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/676); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-243/run-676.md) — updating every ~5 minutes while the task runs.
claude-reviewer approved these changes 2026-10-03 01:51:06 +00:00
Dismissed
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

The phone navigation rework looks coherent. I read the diff and spot-checked app.css, but did not run the app or the tests, so I'm relying on your reported results (build, 584 unit tests, 158 of 159 Playwright tests on the first run).

  • Reduced motion: .nav-menu { transform: none } is safe. The closed sheet is hidden by opacity, visibility and pointer-events in the base rule, not by the transform.
  • Column switcher: board-pagination.js is sound. Updates are batched through rAF, a ResizeObserver re-measures the thumb, and init skips rebuilding the Sortables when the same elements and column count are already wired.
  • Docs and changelog: the docs/ pages are updated with their reasons, and the Changelog entry is in both locales.

Minor, non-blocking: column scroll positions are assumed to sit at i * stride. That holds because all columns have the same width and the board's padding is symmetric, but a column with a different width or padding would put the thumb off. mobile.spec.js covers the common case.


🧠 claude-sonnet-5-5 · ⏱ 0m 17s · 3 turns · tokens: 62.6k in / 1.6k out (+85.4k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. The phone navigation rework looks coherent. I read the diff and spot-checked `app.css`, but did not run the app or the tests, so I'm relying on your reported results (build, 584 unit tests, 158 of 159 Playwright tests on the first run). - **Reduced motion:** `.nav-menu { transform: none }` is safe. The closed sheet is hidden by `opacity`, `visibility` and `pointer-events` in the base rule, not by the transform. - **Column switcher:** `board-pagination.js` is sound. Updates are batched through rAF, a ResizeObserver re-measures the thumb, and `init` skips rebuilding the Sortables when the same elements and column count are already wired. - **Docs and changelog:** the `docs/` pages are updated with their reasons, and the Changelog entry is in both locales. Minor, non-blocking: column scroll positions are assumed to sit at `i * stride`. That holds because all columns have the same width and the board's padding is symmetric, but a column with a different width or padding would put the thumb off. `mobile.spec.js` covers the common case. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 17s · 3 turns · tokens: 62.6k in / 1.6k out (+85.4k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-243/run-676.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:fe1ca2b8bd2f1e1879081731a906ffaba3553ed8 -->
opencode-reviewer requested changes 2026-10-03 01:59:10 +00:00
Dismissed
opencode-reviewer left a comment

🛑 Verdict: request changes — blocking issues below.

The phone redesign holds up well overall: the locale keys (nav.columns, home.cards via TCount, settings.title) all exist, the height arithmetic is internally consistent (--phone-board-title-h: 118px = 56 + 2 + 41 + 1 + 18; Home's head is exactly the 4.5rem the sibling rules subtract; the evenly-divided stride matches the snap positions), Home.razor's Today/FormatDate are pre-existing, the stacking for "sheets over the bar, face above the dim" works out (.board-user is fixed and painted after the pill, the toggle's [aria-expanded=true] z-index keeps it above the backdrop, the search's backdrop at z-index: 150 with the field at 200), the reduced-motion rules disable both the slide and the transform, and no stale .board-dot/sheetRise refs remain anywhere else. Test coverage follows the new shapes closely.

One real blocker: the rename from .board-dot to .board-segment missed the guard in sortable-interop.js, causing a double move on every drop onto a segment — details in the finding.

Minor, non-blocking: --phone-board-title-h is a global 118px, but on the settings pages the bar has no subtitle line (56 + 2 + 41 = 99px), so .board-phone-bar ~ .empty-state over-subtracts by 19px on those min-heights — harmless in practice since those pages are in normal flow, but it means the token comment ("the large title with its line under it") doesn't hold on all pages that render the bar.


⏱ 8m 34s · 19 steps · $0.0695 · tokens: 83.4k in / 2.2k out (+956.9k cache-read) · Go: 5h 4% · wk 9% · mo 87%
📜 Full session transcript

🛑 **Verdict: request changes** — blocking issues below. The phone redesign holds up well overall: the locale keys (`nav.columns`, `home.cards` via `TCount`, `settings.title`) all exist, the height arithmetic is internally consistent (`--phone-board-title-h: 118px` = 56 + 2 + 41 + 1 + 18; Home's head is exactly the 4.5rem the sibling rules subtract; the evenly-divided stride matches the snap positions), `Home.razor`'s `Today`/`FormatDate` are pre-existing, the stacking for "sheets over the bar, face above the dim" works out (`.board-user` is fixed and painted after the pill, the toggle's `[aria-expanded=true]` z-index keeps it above the backdrop, the search's backdrop at `z-index: 150` with the field at 200), the reduced-motion rules disable both the slide and the transform, and no stale `.board-dot`/`sheetRise` refs remain anywhere else. Test coverage follows the new shapes closely. One real blocker: the rename from `.board-dot` to `.board-segment` missed the guard in `sortable-interop.js`, causing a double move on every drop onto a segment — details in the finding. Minor, non-blocking: `--phone-board-title-h` is a global 118px, but on the settings pages the bar has no subtitle line (56 + 2 + 41 = 99px), so `.board-phone-bar ~ .empty-state` over-subtracts by 19px on those min-heights — harmless in practice since those pages are in normal flow, but it means the token comment ("the large title with its line under it") doesn't hold on all pages that render the bar. --- ⏱ 8m 34s · 19 steps · $0.0695 · tokens: 83.4k in / 2.2k out (+956.9k cache-read) · Go: 5h 4% · wk 9% · mo 87% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-243/run-2026-10-03T01-50-36-791Z.md) <!-- opencode-reviewed-head:fe1ca2b8bd2f1e1879081731a906ffaba3553ed8 --> <!-- opencode-verdict:request_changes -->
@ -70,3 +111,3 @@
// and these dots are always empty. The 64px tiles need no halo.
// and a segment never keeps a card. It is target enough without a halo.
emptyInsertThreshold: 0,
onAdd: (evt) => {
Collaborator

This PR renames the drop targets from .board-dot to .board-segment, but the counterpart guard in js/sortable-interop.js (line 172) still reads evt.to.classList.contains('board-dot'), so it never matches any more and its comment's promise — "handling it here too would move it twice" — is now broken.

When a card is dropped on a segment, SortableJS fires both onAdd on the receiving segment (which calls OnCardMoved(cardId, from, to, 9999), appends to the end) and onEnd on the source column's instance, where evt.to is the segment. The stale guard no longer returns early, so the source instance also calls OnCardMoved(cardId, from, toSegment, evt.newIndex) — and newIndex within the always-empty segment is 0. Result: two server moves per drop, and the card ends up at the top of the target column (index 0) instead of the end (9999), with a doubled circuit round trip and re-render. The segments even carry data-column-id, so the second call parses cleanly and goes through.

The new touch-drag test in mobile.spec.js asserts the card lands in the target column but not where, which is why it passes despite the bug. Fix is one line: match .board-segment (or .board-pagination *) in the guard, and update its comment. Worth also pinning the card's position — e.g. that it lands after the target column's existing cards — so a repeat can't sneak past the tests again.

This PR renames the drop targets from `.board-dot` to `.board-segment`, but the counterpart guard in `js/sortable-interop.js` (line 172) still reads `evt.to.classList.contains('board-dot')`, so it never matches any more and its comment's promise — "handling it here too would move it twice" — is now broken. When a card is dropped on a segment, SortableJS fires both `onAdd` on the receiving segment (which calls `OnCardMoved(cardId, from, to, 9999)`, appends to the end) `and` `onEnd` on the source column's instance, where `evt.to` is the segment. The stale guard no longer returns early, so the source instance also calls `OnCardMoved(cardId, from, toSegment, evt.newIndex)` — and `newIndex` within the always-empty segment is `0`. Result: two server moves per drop, and the card ends up at the *top* of the target column (index 0) instead of the end (9999), with a doubled circuit round trip and re-render. The segments even carry `data-column-id`, so the second call parses cleanly and goes through. The new touch-drag test in `mobile.spec.js` asserts the card lands *in* the target column but not *where*, which is why it passes despite the bug. Fix is one line: match `.board-segment` (or `.board-pagination *`) in the guard, and update its comment. Worth also pinning the card's position — e.g. that it lands after the target column's existing cards — so a repeat can't sneak past the tests again.
Match the column switcher's segments in the drag guard, so a drop moves once
All checks were successful
CI / build (pull_request) Successful in 35s
ee6922006c
sortable-interop.js still skipped drops on `.board-dot`, so a card dropped
on a segment was moved by the segment and again by its column's Sortable,
landing on top instead of at the foot. mobile.spec pins where it lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Fixed in ee69220: the guard in sortable-interop.js now matches .board-segment (comment updated), and the touch-drag test in mobile.spec.js now also checks the card ends up last in the target column, which the double move broke by putting it on top.

Not changed: --phone-board-title-h on the settings pages. The bar there has no subtitle, so .board-phone-bar ~ .empty-state subtracts 19px too much from a min-height, but those pages are in normal flow and nothing is clipped. A per-page token would cost more than the 19px.

Fixed in ee69220: the guard in `sortable-interop.js` now matches `.board-segment` (comment updated), and the touch-drag test in `mobile.spec.js` now also checks the card ends up last in the target column, which the double move broke by putting it on top. Not changed: `--phone-board-title-h` on the settings pages. The bar there has no subtitle, so `.board-phone-bar ~ .empty-state` subtracts 19px too much from a min-height, but those pages are in normal flow and nothing is clipped. A per-page token would cost more than the 19px.
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-243/run-2026-10-03T02-03-28-042Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/679); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-243/run-679.md) — updating every ~5 minutes while the task runs.
opencode-reviewer approved these changes 2026-10-03 02:03:40 +00:00
Dismissed
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review: both blockers from the last pass are fixed.

  1. Double move on a drop onto a segment — resolved. sortable-interop.js's onEnd guard now matches .board-segment (line 172), with the comment updated, so the segment's Sortable in board-pagination.js is the sole mover and the card lands at the column's end (9999) with a single circuit round trip.

  2. Test coverage of drop position — resolved. The new touch-drag test in mobile.spec.js now asserts the card arrives at the foot of the target column and that the segment keeps no copy, so the earlier only-checks-it-landed gap is closed and the double-move regression can't pass silently.

Remaining non-blocking notes from the prior passes, unchanged in nature:

  • --phone-board-title-h is 118px globally, but settings pages render the bar with no subtitle line (56 + 2 + 41 = 99px), so .board-phone-bar ~ .empty-state over-subtracts by 19px. Harmless in practice since those pages are in normal flow, but the token's comment ("the large title with its line under it") doesn't hold on every page using the bar.
  • stride() assumes equal column widths and symmetric board padding; holds today and is covered by the common case in the tests, but a differently-sized column would place the thumb off.

Everything else checked out previously and still holds: locale keys exist, the height arithmetic is internally consistent, the sheets-over-bar stacking works, reduced-motion disables both the slide and the transform, and no stale .board-dot/sheetRise references remain. Good to merge.


⏱ 0m 12s · 4 steps · $0.0110 · tokens: 46.7k in / 778 out (+116.6k cache-read) · Go: 5h 4% · wk 9% · mo 87%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Re-review: both blockers from the last pass are fixed. 1. **Double move on a drop onto a segment** — resolved. `sortable-interop.js`'s `onEnd` guard now matches `.board-segment` (line 172), with the comment updated, so the segment's Sortable in `board-pagination.js` is the sole mover and the card lands at the column's end (9999) with a single circuit round trip. 2. **Test coverage of drop position** — resolved. The new touch-drag test in `mobile.spec.js` now asserts the card arrives *at the foot* of the target column and that the segment keeps no copy, so the earlier only-checks-it-landed gap is closed and the double-move regression can't pass silently. Remaining non-blocking notes from the prior passes, unchanged in nature: - `--phone-board-title-h` is 118px globally, but settings pages render the bar with no subtitle line (56 + 2 + 41 = 99px), so `.board-phone-bar ~ .empty-state` over-subtracts by 19px. Harmless in practice since those pages are in normal flow, but the token's comment ("the large title with its line under it") doesn't hold on every page using the bar. - `stride()` assumes equal column widths and symmetric board padding; holds today and is covered by the common case in the tests, but a differently-sized column would place the thumb off. Everything else checked out previously and still holds: locale keys exist, the height arithmetic is internally consistent, the sheets-over-bar stacking works, reduced-motion disables both the slide and the transform, and no stale `.board-dot`/`sheetRise` references remain. Good to merge. --- ⏱ 0m 12s · 4 steps · $0.0110 · tokens: 46.7k in / 778 out (+116.6k cache-read) · Go: 5h 4% · wk 9% · mo 87% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-243/run-2026-10-03T02-03-28-042Z.md) <!-- opencode-reviewed-head:ee6922006c232165d5728bf47a82c0e596a64b04 --> <!-- opencode-verdict:approve -->
claude-reviewer approved these changes 2026-10-03 02:03:49 +00:00
Dismissed
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

I found nothing new that blocks merging. My earlier pass raised no blocking issues, so there was nothing to confirm as fixed. I re-read the diff but did not run the app or the tests, so I'm relying on your reported results.

  • Column switcher: board-pagination.js still looks sound. Updates are batched through rAF, the ResizeObserver re-measures the thumb, and init doesn't rebuild the Sortables when the same elements and column count are already wired.
  • Sheets and reduced motion: the covering sheets, the face staying above the dim, and the reduced-motion fallbacks are consistent with the docs.
  • Docs and changelog: the docs/ pages are updated with their reasons, and the Changelog entry is in both locales.
  • Non-blocking: the thumb assumes columns sit at i * stride, which holds only while all columns have the same width and the board's padding is symmetric.

🧠 claude-sonnet-5-5 · ⏱ 0m 7s · 2 turns · tokens: 62.1k in / 539 out (+12.2k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. I found nothing new that blocks merging. My earlier pass raised no blocking issues, so there was nothing to confirm as fixed. I re-read the diff but did not run the app or the tests, so I'm relying on your reported results. - **Column switcher:** `board-pagination.js` still looks sound. Updates are batched through rAF, the ResizeObserver re-measures the thumb, and `init` doesn't rebuild the Sortables when the same elements and column count are already wired. - **Sheets and reduced motion:** the covering sheets, the face staying above the dim, and the reduced-motion fallbacks are consistent with the docs. - **Docs and changelog:** the `docs/` pages are updated with their reasons, and the Changelog entry is in both locales. - **Non-blocking:** the thumb assumes columns sit at `i * stride`, which holds only while all columns have the same width and the board's padding is symmetric. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 7s · 2 turns · tokens: 62.1k in / 539 out (+12.2k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-243/run-679.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:ee6922006c232165d5728bf47a82c0e596a64b04 -->
Grandiras force-pushed ui-v2-phone from ee6922006c
All checks were successful
CI / build (pull_request) Successful in 35s
to 37d45bda6f
All checks were successful
CI / build (pull_request) Successful in 47s
2026-10-03 02:12:03 +00:00
Compare
Collaborator

🤖 Reviewing this PR — the review will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the review runs.

🤖 Reviewing this PR — the review will link the full session transcript. <!-- opencode-run:review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-243/run-2026-10-03T02-13-04-122Z.md) — updating every ~5 minutes while the review runs.
Collaborator

🤖 Reviewing this PR — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 Reviewing this PR — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/681); the reply will link the full session transcript. <!-- claude-run:pr_review --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-243/run-681.md) — updating every ~5 minutes while the task runs.
claude-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

I found nothing new that blocks merging. My earlier passes raised no blocking issues, so there was nothing to confirm as fixed. I re-read the diff but did not run the app or the tests, so I'm relying on your reported results (build, 584 unit tests, 158 of 159 Playwright tests on the first run).

  • Column switcher: board-pagination.js still looks sound. Updates are batched through rAF, the ResizeObserver re-measures the thumb, and init doesn't rebuild the Sortables when the same elements and column count are already wired.
  • Sheets and reduced motion: the sheets covering the tab bar, the face staying above the dim, and the reduced-motion fallbacks are consistent with the docs.
  • Docs and changelog: the docs/ pages are updated with their reasons, and the Changelog entry is in both locales.
  • Non-blocking: the thumb assumes column i sits at i * stride. That holds only while all columns have the same width and the board's padding is symmetric.

🧠 claude-sonnet-5-5 · ⏱ 0m 5s · 2 turns · tokens: 62.6k in / 439 out (+12.2k cache-read)
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. I found nothing new that blocks merging. My earlier passes raised no blocking issues, so there was nothing to confirm as fixed. I re-read the diff but did not run the app or the tests, so I'm relying on your reported results (build, 584 unit tests, 158 of 159 Playwright tests on the first run). - **Column switcher:** `board-pagination.js` still looks sound. Updates are batched through rAF, the ResizeObserver re-measures the thumb, and `init` doesn't rebuild the Sortables when the same elements and column count are already wired. - **Sheets and reduced motion:** the sheets covering the tab bar, the face staying above the dim, and the reduced-motion fallbacks are consistent with the docs. - **Docs and changelog:** the `docs/` pages are updated with their reasons, and the Changelog entry is in both locales. - **Non-blocking:** the thumb assumes column `i` sits at `i * stride`. That holds only while all columns have the same width and the board's padding is symmetric. --- 🧠 claude-sonnet-5-5 · ⏱ 0m 5s · 2 turns · tokens: 62.6k in / 439 out (+12.2k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/pr-243/run-681.md) <!-- claude-verdict:approve --> <!-- claude-reviewed-head:37d45bda6ffafe69d97be07441ebec0975c008b0 -->
opencode-reviewer left a comment

✅ Verdict: approve — no blocking issues found.

Re-review: both blockers from the previous pass are fixed, and this is good to merge.

  1. Double move on a drop onto a segment — resolved. sortable-interop.js's onEnd guard now matches .board-segment (line 172) with the comment updated, so the segment's own Sortable (board-pagination.js) is the sole mover and the card lands at the column's end (9999) in one circuit round trip. No .board-dot references remain anywhere.
  2. Drop position unpinned by tests — resolved. The touch-drag test in mobile.spec.js now asserts the card arrives at the foot of the target column and that the segment keeps no copy, so the double-move regression cannot pass again.

Also re-verified the smaller checks from earlier passes: the locale keys (nav.columns, home.cards/home.cards.one, settings.title) exist in both locales, --brand-glow is still defined for the segment drop highlight, sheetRise is fully gone from app.css, and the height arithmetic and sheets-over-bar stacking hold as before.

Non-blocking notes, unchanged:

  • --phone-board-title-h is a global 118px, but settings pages render the bar without the subtitle line (56 + 2 + 41 = 99px), so .board-phone-bar ~ .empty-state over-subtracts by 19px there. Harmless since those pages are in normal flow, but the token's comment only holds on board pages.
  • stride() in board-pagination.js assumes equal column widths and symmetric board padding; true today and covered for the common case, but a narrower column would put the thumb off.

⏱ 0m 45s · 7 steps · $0.0156 · tokens: 44.8k in / 1.0k out (+246.0k cache-read) · Go: 5h 5% · wk 9% · mo 87%
📜 Full session transcript

✅ **Verdict: approve** — no blocking issues found. Re-review: both blockers from the previous pass are fixed, and this is good to merge. 1. **Double move on a drop onto a segment** — resolved. `sortable-interop.js`'s `onEnd` guard now matches `.board-segment` (line 172) with the comment updated, so the segment's own Sortable (`board-pagination.js`) is the sole mover and the card lands at the column's end (9999) in one circuit round trip. No `.board-dot` references remain anywhere. 2. **Drop position unpinned by tests** — resolved. The touch-drag test in `mobile.spec.js` now asserts the card arrives *at the foot* of the target column and that the segment keeps no copy, so the double-move regression cannot pass again. Also re-verified the smaller checks from earlier passes: the locale keys (`nav.columns`, `home.cards`/`home.cards.one`, `settings.title`) exist in both locales, `--brand-glow` is still defined for the segment drop highlight, `sheetRise` is fully gone from `app.css`, and the height arithmetic and sheets-over-bar stacking hold as before. Non-blocking notes, unchanged: - `--phone-board-title-h` is a global 118px, but settings pages render the bar without the subtitle line (56 + 2 + 41 = 99px), so `.board-phone-bar ~ .empty-state` over-subtracts by 19px there. Harmless since those pages are in normal flow, but the token's comment only holds on board pages. - `stride()` in `board-pagination.js` assumes equal column widths and symmetric board padding; true today and covered for the common case, but a narrower column would put the thumb off. --- ⏱ 0m 45s · 7 steps · $0.0156 · tokens: 44.8k in / 1.0k out (+246.0k cache-read) · Go: 5h 5% · wk 9% · mo 87% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/pr-243/run-2026-10-03T02-13-04-122Z.md) <!-- opencode-reviewed-head:37d45bda6ffafe69d97be07441ebec0975c008b0 --> <!-- opencode-verdict:approve -->
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
5 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Grandiras/Ponente!243
No description provided.