fix(view): make the log list the scroll container #48

Open
omp-deck wants to merge 1 commit from agent/issue-43-log-list-scroller into master
Collaborator

Closes: #43

The log list, not the window, now owns the vertical scrolling, and nowrap rows paint across the full scroll width.

What changed

Scroller. body:has(.log-list) becomes a flex column of 100dvh; the section and its container pass the bound down, and .log-list takes what the chrome leaves (flex: 1; min-height: 0). The overflow-y: auto that overflow-x: auto already implied is now the vertical scroller.

This is a flex chain rather than height: calc(100dvh - navbar - view-bar): the view bar's height is not a constant (title line vs. .button.is-small metrics), and the issue names both failure modes of guessing it wrong — a double scrollbar or a clipped last row. The flex chain derives it instead. Scoped on :has(.log-list) so the error and empty states keep the window scroller and stay fully readable: an upstream error is surfaced verbatim and untruncated, and must not be clipped by a viewport-height body.

Chrome. position: sticky / top / z-index are gone from .navbar and .view-bar, together with the opaque background on the view bar that existed only to cover what was scrolling underneath it. Both bars sit in normal flow; the hairline rules stay. The zero-padding rules at the section and container stay too, but their comment no longer cites the Firefox sticky-strip hazard, which no longer applies — only the edge-to-edge density argument remains.

Nowrap width. width: max-content; min-width: 100% moves off .log-row and onto the list as grid-template-columns: minmax(max-content, 1fr). A row's own min-width: 100% resolves against the list's visible width, never its scroll width, which is exactly why short rows stopped painting where the scrolled-to region began. The track holds at the widest row's width and grows to fill the list when every row is narrower; rows stretch to it either way.

The padding-bottom that keeps the newest row clear of Firefox's overlay horizontal scrollbar is still needed — with the list as scroller the bar overlays the list's own bottom edge — and is joined by a matching scroll-padding-bottom, otherwise scrollIntoView would align the row with the scrollport edge and put it straight back under the bar.

JS. window.scrollTo(0, document.body.scrollHeight) → list.lastElementChild?.scrollIntoView({ block: "end" }).

Verification

just test (12 + 2 integration tests, unit tests — no assertion moved), cargo clippy --all-targets -- -D warnings, cargo fmt --check, stylelint and Biome all pass.

None of the behaviour above is observable to that harness, and CI has no browser, so it was measured in headless Chromium against the compiled stylesheet and a fixture reproducing the rendered page (200 rows, one much wider than the viewport, 1280×800):

Property Result
window scrolls no — documentScrollHeight == innerHeight
navbar / view-bar position relative (Bulma's own) / static
list scrolls vertically yes — scrollHeight 4712 vs clientHeight 696
nowrap horizontal scroll yes — scrollWidth 3371 vs clientWidth 1280
row width at any offset 3371 = full scroll width, for the shortest row as for the widest
wrap mode no horizontal overflow (scrollWidth == clientWidth)
narrow content only track fills the list (1280), rows span it, no scrollbars at all
opens at the newest row scrollTop == maxScrollTop, last row's bottom 12px (0.75rem) above the list's edge

Firefox's overlay scrollbar itself is not reproducible headless, so the padding-bottom / scroll-padding-bottom pair still wants a look in a real Firefox.

The .is-ready opacity gate is untouched; tests/views.rs asserts rendered HTML only and needed no change.

Not done here

docs/STATUS.md is stale in ways unrelated to this change (it claims src/views/, templates/views/ and templates/partials/log_row.html do not exist). Left alone, as the issue asks.

Closes: #43 The log list, not the window, now owns the vertical scrolling, and nowrap rows paint across the full scroll width. ## What changed **Scroller.** `body:has(.log-list)` becomes a flex column of `100dvh`; the section and its container pass the bound down, and `.log-list` takes what the chrome leaves (`flex: 1; min-height: 0`). The `overflow-y: auto` that `overflow-x: auto` already implied is now the vertical scroller. This is a flex chain rather than `height: calc(100dvh - navbar - view-bar)`: the view bar's height is not a constant (title line vs. `.button.is-small` metrics), and the issue names both failure modes of guessing it wrong — a double scrollbar or a clipped last row. The flex chain derives it instead. Scoped on `:has(.log-list)` so the error and empty states keep the window scroller and stay fully readable: an upstream error is surfaced verbatim and untruncated, and must not be clipped by a viewport-height body. **Chrome.** `position: sticky` / `top` / `z-index` are gone from `.navbar` and `.view-bar`, together with the opaque `background` on the view bar that existed only to cover what was scrolling underneath it. Both bars sit in normal flow; the hairline rules stay. The zero-padding rules at the section and container stay too, but their comment no longer cites the Firefox sticky-strip hazard, which no longer applies — only the edge-to-edge density argument remains. **Nowrap width.** `width: max-content; min-width: 100%` moves off `.log-row` and onto the list as `grid-template-columns: minmax(max-content, 1fr)`. A row's own `min-width: 100%` resolves against the list's *visible* width, never its scroll width, which is exactly why short rows stopped painting where the scrolled-to region began. The track holds at the widest row's width and grows to fill the list when every row is narrower; rows stretch to it either way. The `padding-bottom` that keeps the newest row clear of Firefox's overlay horizontal scrollbar is still needed — with the list as scroller the bar overlays the list's own bottom edge — and is joined by a matching `scroll-padding-bottom`, otherwise `scrollIntoView` would align the row with the scrollport edge and put it straight back under the bar. **JS.** `window.scrollTo(0, document.body.scrollHeight)` → `list.lastElementChild?.scrollIntoView({ block: "end" })`. ## Verification `just test` (12 + 2 integration tests, unit tests — no assertion moved), `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check`, stylelint and Biome all pass. None of the behaviour above is observable to that harness, and CI has no browser, so it was measured in headless Chromium against the compiled stylesheet and a fixture reproducing the rendered page (200 rows, one much wider than the viewport, 1280×800): | Property | Result | |---|---| | window scrolls | no — `documentScrollHeight == innerHeight` | | navbar / view-bar `position` | `relative` (Bulma's own) / `static` | | list scrolls vertically | yes — `scrollHeight` 4712 vs `clientHeight` 696 | | nowrap horizontal scroll | yes — `scrollWidth` 3371 vs `clientWidth` 1280 | | row width at any offset | 3371 = full scroll width, for the shortest row as for the widest | | wrap mode | no horizontal overflow (`scrollWidth == clientWidth`) | | narrow content only | track fills the list (1280), rows span it, no scrollbars at all | | opens at the newest row | `scrollTop == maxScrollTop`, last row's bottom 12px (`0.75rem`) above the list's edge | Firefox's overlay scrollbar itself is not reproducible headless, so the `padding-bottom` / `scroll-padding-bottom` pair still wants a look in a real Firefox. The `.is-ready` opacity gate is untouched; `tests/views.rs` asserts rendered HTML only and needed no change. ## Not done here `docs/STATUS.md` is stale in ways unrelated to this change (it claims `src/views/`, `templates/views/` and `templates/partials/log_row.html` do not exist). Left alone, as the issue asks.
fix(view): make the log list the scroll container
All checks were successful
pr / assets (pull_request) Successful in 9s
pr / lint (pull_request) Successful in 10s
pr / check (pull_request) Successful in 49s
pr / test (pull_request) Successful in 1m1s
pr / image (pull_request) Successful in 9m14s
446003a583
The window owned the vertical scrolling, and the chrome compensated by
pinning the navbar and the view bar with `position: sticky`. That blocks
the column header row planned in #39: the header has to sit inside the
list, or it desyncs from the columns as soon as nowrap mode scrolls
sideways, and it has to stay visible while the log scrolls. Because
`overflow-x: auto` makes the list a scroll container on both axes, a
sticky header inside it can only stick against the list's own
scrollport, which does not scroll while the window is the scroller.
Of the three requirements, window-as-scroller is the cheapest to drop:
the sticky positioning existed only to compensate for it.

The body of a view page becomes a flex column of viewport height and
passes that bound down to the list, which is bounded by what the chrome
above it leaves rather than by a hardcoded chrome height that would
drift into a double scrollbar or a clipped last row. `dvh` covers mobile
browser chrome resizing.

Nowrap rows were sized `width: max-content; min-width: 100%`, so a row
shorter than the scroll width had no background under the scrolled-to
region -- barely visible with only a hover highlight, a staircase once
#42 paints every row. The width moves from the rows to a grid column
sized `minmax(max-content, 1fr)`, since a row's own percentage min-width
resolves against the list's visible width and never its scroll width.

Scrolling to the newest row now targets the list. `scrollIntoView`
rather than `scrollTop = scrollHeight`: equivalent today, but it forces
the target to render, so it survives a later `content-visibility`. The
scrollbar gap the newest row needs becomes scroll padding as well, or
the scroll would place the row right back under the bar.

Refs: #43
Co-Authored-By: Claude Opus 5 via omp-deck
All checks were successful
pr / assets (pull_request) Successful in 9s
Required
Details
pr / lint (pull_request) Successful in 10s
Required
Details
pr / check (pull_request) Successful in 49s
Required
Details
pr / test (pull_request) Successful in 1m1s
Required
Details
pr / image (pull_request) Successful in 9m14s
Required
Details
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin 'agent/issue-43-log-list-scroller:agent/issue-43-log-list-scroller'
git switch 'agent/issue-43-log-list-scroller'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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
lucas/logview!48
No description provided.