Files
the-daily-epub/docs/plans/briefs/ui-pass/handoff-toc.md
T
thalladaandClaude Fable 5.1 ad2e99eeb9 UI pass: table-of-contents sidebar fixes and scroll sync
- The open hamburger panel now fills the viewport below the sticky bar
  (100dvh, measured bar offset) and locks the page behind it. Locking only
  the root element: locking body too made it its own scroll container and
  un-stuck the bar.
- Front-page scroll-spy: the current chapter marker, mobile label/counter,
  desktop "Chapter N of M" line and both progress bars follow the reader
  through "In This Issue"; passed chapters read in ink. The sidebar
  auto-scrolls to keep the current chapter in view.
- The sidebar scrollbar sits in a reserved gutter instead of over the text.
- Hamburger cross-fades to a close icon; 44px tap targets on phones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MD4VWGq6mGcd8Bg67qyx9k
2026-09-04 17:05:39 +00:00

90 lines
4.9 KiB
Markdown

# Task 1 handoff — table-of-contents sidebar
## What changed
- `src/web/templates/_toc.html`
- Reworked the narrow-screen panel to use a measured viewport offset and a
`100dvh`-based height instead of the old `72vh` cap.
- Added live label, count, desktop-status, link-label, and per-link progress
hooks used by the shared TOC state updater.
- Moved current-link presentation onto `.toc-link[aria-current]` and kept the
server-rendered `aria-current="page"` state for chapter pages.
- Added both hamburger and close SVGs for the open-state cross-fade and raised
the mobile toggle/link hit areas to at least 44 px.
- Added explicit right padding around the scrollable list.
- `src/web/templates/issue_full.html`
- Marked The Brief, every article index item, and the colophon with matching
`data-toc-entry` values for the front-page scroll-spy.
- `src/web/static/app.js`
- Added mobile panel sizing from the sticky bar's current viewport position,
recalculated on open and layout/visual-viewport resize.
- Added mobile document scroll lock and retained toggle, Escape,
outside-click, link-click, and `lg` breakpoint close behavior.
- Added a passive, requestAnimationFrame-throttled front-page scroll-spy. A
single current-link update now drives `aria-current="location"`, the mobile
title/count, desktop chapter status, both progress bars, and TOC reveal.
- Updated current-link reveal to use `scrollTo`, smooth unless reduced motion
is requested, without scrolling the page.
- Preserved progress-within-chapter behavior on article pages.
- `src/web/tailwind.css`
- Added the dynamic mobile panel height and scroll-lock rules.
- Added stable scrollbar gutter, right-side spacing, thin/rule-colored
cross-browser scrollbar styling, and overscroll containment.
- Added the centralized current/hover/focus link styles and exact 150 ms
opacity/scale/blur hamburger-to-close transition.
- `src/web/static/app.css`
- Rebuilt from the Tailwind source.
- `src/web/issue.rs`
- Added each TOC item's semantic progress position (including back matter) and
coverage for those positions and the rendered scroll-spy hooks.
- Made the current-link router assertion insensitive to HTML attribute order.
## Design decisions
- The open panel remains positioned directly below the shared sticky bar. JS
measures only the bar's on-screen bottom edge into `--toc-panel-top`; CSS owns
the final `calc(100dvh - var(--toc-panel-top))` height. This handles both the
bar still below the masthead and the bar already stuck at the top.
- The Brief uses progress position 0. Article, World, and Behind entries use
their chapter position. The colophon uses the issue total, so reaching it
communicates completion even though World and Behind are separate pages.
- Front-page position changes only toggle `aria-current` for current styling;
the other text/progress surfaces are derived from that same link's label and
progress metadata.
- Scrollbar gutter and padding are both used: the gutter reserves space for
classic scrollbars, while padding protects labels on overlay-scrollbar
platforms.
## Deviations and open items
- No live screenshots could be captured. The sandbox rejected the assigned
`127.0.0.1:3601` bind with `Operation not permitted`, exactly as anticipated
by the shared brief. Consequently desktop/mobile, light/dark, open-panel, and
scripted-scroll visual states remain to be checked outside the sandbox.
- The full unit test run also found three OpenAI mock-server tests blocked by
loopback binding in addition to the bind-dependent tests listed in the shared
brief. They fail at the same listener creation point and are unrelated to this
change.
## Verification
- `node --check src/web/static/app.js` — passed.
- `cargo fmt --check` — passed.
- `npm run css` — passed with Tailwind 4.3.3; `src/web/static/app.css` rebuilt.
- `npm run css:check` — passed with no diff. The worktree lacked its own CLI
shim, so both npm commands used the already-installed adjacent checkout's
`node_modules/.bin` via `PATH`; all inputs and outputs stayed in this worktree.
- `cargo test --lib web::` — 83 passed, 0 failed.
- `cargo clippy --all-targets -- -D warnings` — passed.
- `cargo test` — library phase: 431 passed, 13 bind-dependent failures. The
failures were 7 LLM mock-server tests, the documented relative-URL extraction
test, and the documented 5 server tests; each failed with `Operation not
permitted` while creating a loopback listener.
- `cargo test --test config_check --test e2e_pipeline --test m2_pipeline --test m3_curation --test m4_epub`
— 25 passed, 0 failed. `m7_server` was excluded because it requires the same
forbidden loopback bind.
- Attempted the configured dev-server command on port 3601 — build passed; bind
failed with `Operation not permitted`, so screenshots were not possible.
- `git diff --check` — passed.