The phone's navigation sheet scrolls without saying so #210

Closed
opened 2026-08-23 11:47:01 +00:00 by logan · 2 comments
Collaborator

Report

Since #71 the phone's "More" navigation is a bottom sheet, and on the
reference device's viewport it does not fit: measured at 424x439 with
the seed's eight visible destinations, the sheet's body is
scrollHeight 412 against clientHeight 373. So 39px — most of a row —
is below the fold, reachable by a drag and announced by nothing. Where
the cut lands on a row boundary the sheet ends in a clean edge that
reads as the end of the list, which is the same symptom #207 was filed
for one surface over.

It gets worse with the destinations the seed does not show: eleven items
at 48px is 528px of content in a 373px body, and #25 means the count is
the user's.

Reproduction

make dev-headless SEED=default
playwright-cli -s=yj open http://localhost:34115
playwright-cli -s=yj resize 424 439
playwright-cli -s=yj click "[data-testid='tab-more']"
playwright-cli -s=yj eval "() => { const b = document.querySelector('bottom-nav') \
  .shadowRoot.querySelector('wa-drawer').shadowRoot.querySelector('[part~=body]'); \
  return [b.scrollHeight, b.clientHeight, getComputedStyle(b).backgroundImage]; }"
# [412, 373, "none"]

Findings

  • #207 answers exactly this question for menu-surface's context
    sheet, and its answer needs no scroll listener and no measurement:
    two background layers on the scrolling box, whose attachments are
    the conditionality — a cover of the sheet's own colour painted at the
    end of the content (local) over a shadow pinned to the box
    (scroll), so the cover scrolls up and hides the shadow exactly when
    there is nothing more to see. background-attachment is Chrome 4;
    the device is Chrome 113.
  • It was deliberately not copied into bottom-nav while #207 was
    an open PR: a second copy of an unshipped rule is how two sheets come
    to disagree about what a fold looks like. The measurements, including
    the contrast argument for why the curve is steep rather than linear,
    are in .planning/NOTES.md (2026-08-23).
  • The two sheets have different colours — the nav sheet takes the
    sidebar's --yj-bg-surface, the context sheet the menus'
    --yj-bg-elevated — so whatever this becomes is parameterised on the
    surface colour rather than hard-coding one.

Direction

Once #207 has merged, put its two layers somewhere both sheets read
rather than in each of them: a css fragment in styles/ taking the
sheet's own colour, adopted by menu-surface and by bottom-nav.
Assert it the way #207 does — the component tier can see
background-attachment, and the rendered result needs pixels.

Depends on #207 in the ordinary sense (it is where the rule comes
from), and the shared-fragment half of it is the whole work here.

**Report** Since #71 the phone's "More" navigation is a bottom sheet, and on the reference device's viewport it does not fit: measured at 424x439 with the seed's eight visible destinations, the sheet's body is `scrollHeight` 412 against `clientHeight` 373. So 39px — most of a row — is below the fold, reachable by a drag and announced by nothing. Where the cut lands on a row boundary the sheet ends in a clean edge that reads as the end of the list, which is the same symptom #207 was filed for one surface over. It gets worse with the destinations the seed does not show: eleven items at 48px is 528px of content in a 373px body, and #25 means the count is the user's. **Reproduction** ``` make dev-headless SEED=default playwright-cli -s=yj open http://localhost:34115 playwright-cli -s=yj resize 424 439 playwright-cli -s=yj click "[data-testid='tab-more']" playwright-cli -s=yj eval "() => { const b = document.querySelector('bottom-nav') \ .shadowRoot.querySelector('wa-drawer').shadowRoot.querySelector('[part~=body]'); \ return [b.scrollHeight, b.clientHeight, getComputedStyle(b).backgroundImage]; }" # [412, 373, "none"] ``` **Findings** - #207 answers exactly this question for `menu-surface`'s context sheet, and its answer needs no scroll listener and no measurement: two background layers on the scrolling box, whose *attachments* are the conditionality — a cover of the sheet's own colour painted at the end of the content (`local`) over a shadow pinned to the box (`scroll`), so the cover scrolls up and hides the shadow exactly when there is nothing more to see. `background-attachment` is Chrome 4; the device is Chrome 113. - It was deliberately **not** copied into `bottom-nav` while #207 was an open PR: a second copy of an unshipped rule is how two sheets come to disagree about what a fold looks like. The measurements, including the contrast argument for why the curve is steep rather than linear, are in `.planning/NOTES.md` (2026-08-23). - The two sheets have different colours — the nav sheet takes the sidebar's `--yj-bg-surface`, the context sheet the menus' `--yj-bg-elevated` — so whatever this becomes is parameterised on the surface colour rather than hard-coding one. **Direction** Once #207 has merged, put its two layers somewhere both sheets read rather than in each of them: a `css` fragment in `styles/` taking the sheet's own colour, adopted by `menu-surface` and by `bottom-nav`. Assert it the way #207 does — the component tier can see `background-attachment`, and the rendered result needs pixels. Depends on #207 in the ordinary sense (it is where the rule comes from), and the shared-fragment half of it is the whole work here.
logan self-assigned this 2026-08-26 08:33:43 +00:00
logan added the
Status
In Progress
label 2026-08-26 08:33:44 +00:00
Author
Collaborator

Picking this up now that #207 has merged, which is what it was waiting for.

Approach, straight from the Direction: lift #207's two background layers out
of menu-surface into a css fragment under frontend/src/styles/,
parameterised on the sheet's own colour rather than hard-coding the menus'
--yj-bg-elevated, and adopt it in both menu-surface's wa-dialog::part(body)
and bottom-nav's wa-drawer::part(body) — one rule, two sheets, so the two
cannot come to disagree about what a fold looks like.

Verifying in the component tier the way #207 does (the parsed
background-attachment pair is the mechanism), plus a rendered measurement of
the nav sheet at 424x439 against make dev-headless.

Picking this up now that #207 has merged, which is what it was waiting for. Approach, straight from the Direction: lift #207's two background layers out of `menu-surface` into a `css` fragment under `frontend/src/styles/`, parameterised on the sheet's own colour rather than hard-coding the menus' `--yj-bg-elevated`, and adopt it in both `menu-surface`'s `wa-dialog::part(body)` and `bottom-nav`'s `wa-drawer::part(body)` — one rule, two sheets, so the two cannot come to disagree about what a fold looks like. Verifying in the component tier the way #207 does (the parsed `background-attachment` pair is the mechanism), plus a rendered measurement of the nav sheet at 424x439 against `make dev-headless`.
Author
Collaborator

PR: #222 — CI green (check 22194, e2e 22196). Not merged; leaving Status/In Progress on.

Done as the Direction asks: #207's two layers are frontend/src/styles/sheet-scroll.css.ts now, adopted by menu-surface and by bottom-nav, with the colour left to each host as --yj-sheet-surface — the nav sheet paints the sidebar's --yj-bg-surface, the context sheet the menus' --yj-bg-elevated.

One thing this issue does not name, and it is the half that made the difference. With the fragment adopted, every assertion about background-attachment passed and the running app still drew a flat 52,58,64 to the bottom edge with 39px below the fold: these are background layers on the scroller, and app-sidebar's host paints --yj-bg-surface, which inside the sheet is a second opaque copy of the sheet's own surface sitting over them. bottom-nav turns it off, which is .context-menu-panel[data-sheet]'s background-color: transparent met from the other side. Anyone copying this rule to a third sheet needs that half too, so the component tier asserts the sidebar's computed background beside the attachments.

Measured after, at 424x439 (body scrollHeight 412 / clientHeight 373, this issue's own numbers), x=300 on the dark ramp: 33,37,41 at y=400 falling to 14,15,17 at the bottom edge with more below, and flat with the last destination fully drawn once scrolled to the end. Light ramp: the last fully visible label sits at 13.0:1.

PR: https://git.ljones.me/yonlu/yellowjacket/pulls/222 — CI green (`check` 22194, `e2e` 22196). Not merged; leaving `Status/In Progress` on. Done as the Direction asks: #207's two layers are `frontend/src/styles/sheet-scroll.css.ts` now, adopted by `menu-surface` and by `bottom-nav`, with the colour left to each host as `--yj-sheet-surface` — the nav sheet paints the sidebar's `--yj-bg-surface`, the context sheet the menus' `--yj-bg-elevated`. One thing this issue does not name, and it is the half that made the difference. With the fragment adopted, every assertion about `background-attachment` passed and the running app still drew **a flat 52,58,64 to the bottom edge with 39px below the fold**: these are background layers on the scroller, and `app-sidebar`'s host paints `--yj-bg-surface`, which inside the sheet is a second *opaque* copy of the sheet's own surface sitting over them. `bottom-nav` turns it off, which is `.context-menu-panel[data-sheet]`'s `background-color: transparent` met from the other side. Anyone copying this rule to a third sheet needs that half too, so the component tier asserts the sidebar's computed background beside the attachments. Measured after, at 424x439 (body `scrollHeight` 412 / `clientHeight` 373, this issue's own numbers), x=300 on the dark ramp: 33,37,41 at y=400 falling to 14,15,17 at the bottom edge with more below, and flat with the last destination fully drawn once scrolled to the end. Light ramp: the last fully visible label sits at 13.0:1.
logan closed this issue 2026-08-26 16:03:11 +00:00
gitea-actions bot removed the
Status
In Progress
label 2026-08-26 16:03:33 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#210