feat(ui): F8-09 — turn report sticky icon-popup section menu #67
+25
-4
@@ -85,8 +85,8 @@ the body — both surfaces stay in sync by construction.
|
||||
|
||||
## Table of contents and active highlight
|
||||
|
||||
`report/report-toc.svelte` renders a single sticky icon-popup
|
||||
trigger in the top-right corner of the report column. The trigger
|
||||
`report/report-toc.svelte` renders a single icon-popup trigger
|
||||
pinned to the top-right corner of the report column. The trigger
|
||||
shows `≡` followed by the title of the currently-active section
|
||||
(CSS-clamped with `text-overflow: ellipsis` so a long RU title
|
||||
cannot bloat the button). Clicking opens an anchored popover
|
||||
@@ -97,8 +97,29 @@ the matching `<section id="report-<slug>">` into view via
|
||||
`scrollIntoView` (with `prefers-reduced-motion` falling back to
|
||||
`behavior: "auto"`).
|
||||
|
||||
On viewports below `768.98 px` the same surface re-styles into a
|
||||
fixed bottom-sheet anchored above the layout-owned bottom-tabs
|
||||
The trigger uses `position: fixed` instead of `position: sticky`.
|
||||
Per the CSS sticky spec a sticky element sticks within its nearest
|
||||
ancestor with non-`visible` overflow — and `.active-view-host`
|
||||
declares `overflow-y: auto` for the mobile scroll story. On
|
||||
desktop the host grows with content and the document body becomes
|
||||
the actual scroll container, so a sticky trigger inside the report
|
||||
column never receives a scroll event and rides up with the page
|
||||
content. A fixed trigger sidesteps the chain entirely; the
|
||||
component is mounted only while the report active view is on
|
||||
screen, so the fixed element is naturally tied to the view's
|
||||
lifetime. The desktop offset is `right: calc(18 rem + 1.25 rem)`
|
||||
to clear the always-on `lib/sidebar/sidebar.svelte`; below
|
||||
1024 px the sidebar collapses to an overlay drawer, so the
|
||||
default `right: 1.25 rem` matches the report's right padding. The
|
||||
report-view itself adds a top padding equal to the trigger's
|
||||
viewport offset plus its height so the first section's heading
|
||||
does not render under the trigger at scroll position 0, and every
|
||||
`<section id="report-…">` gets `scroll-margin-top: 7.5rem` so
|
||||
`scrollIntoView({ block: "start" })` lands the heading below the
|
||||
trigger after a popover-driven jump.
|
||||
|
||||
On viewports below `768.98 px` the popover surface re-styles into
|
||||
a fixed bottom-sheet anchored above the layout-owned bottom-tabs
|
||||
bar (mirrors `lib/active-view/map-toggles.svelte`), so the same
|
||||
trigger and the same menuitem list serve desktop and mobile.
|
||||
|
||||
|
||||
@@ -134,10 +134,14 @@ TOC and the body iterate the same data.
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/*
|
||||
`padding-top` clears the fixed TOC trigger that lives in the
|
||||
viewport's top-right corner (`report/report-toc.svelte`,
|
||||
`position: fixed; top: 4rem`). Without this padding the first
|
||||
section (galaxy-summary) renders directly under the trigger.
|
||||
*/
|
||||
.report-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 1.25rem 2rem;
|
||||
padding: 4.5rem 1.25rem 2rem;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
.report-body {
|
||||
@@ -146,9 +150,18 @@ TOC and the body iterate the same data.
|
||||
flex-direction: column;
|
||||
gap: 1.75rem;
|
||||
}
|
||||
/*
|
||||
`scroll-margin-top` lets `scrollIntoView({ block: "start" })`
|
||||
land the section *heading* in view (not behind the fixed
|
||||
trigger or the sticky in-game header). Budget: ~3 rem header +
|
||||
~3 rem trigger area + ~1.5 rem breathing room.
|
||||
*/
|
||||
.report-body :global(section[id^="report-"]) {
|
||||
scroll-margin-top: 7.5rem;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.report-view {
|
||||
padding: 0.75rem;
|
||||
padding: 4rem 0.75rem 0.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -143,14 +143,43 @@ in F8-09: that switch is available in the app-shell view menu.
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/*
|
||||
The trigger uses `position: fixed` rather than `position: sticky`
|
||||
because on desktop the in-game shell's `.active-view-host`
|
||||
declares `overflow-y: auto` while staying tall enough that no
|
||||
overflow actually engages — the document body scrolls instead.
|
||||
Per the CSS sticky spec the host is the "scrollport" for any
|
||||
sticky descendant, so a sticky trigger inside the report column
|
||||
never receives a scroll event and rides up with the page. Fixed
|
||||
positioning anchors the trigger to the viewport directly; the
|
||||
component is only mounted while the report active view is on
|
||||
screen, so the fixed element is naturally tied to the view's
|
||||
lifetime.
|
||||
|
||||
`top: 4rem` clears the sticky header (~3 rem). On ≥ 1024 px the
|
||||
sidebar (`lib/sidebar/sidebar.svelte`) is always visible and
|
||||
occupies the right 18 rem of the viewport, so the trigger has to
|
||||
be pushed left by that much to stay inside the report column.
|
||||
Below 1024 px the sidebar collapses into an overlay drawer,
|
||||
so a viewport-right anchor of 1.25 rem matches the report's
|
||||
right padding.
|
||||
|
||||
When the history banner is showing (historical turn view,
|
||||
~2 rem extra) the trigger sits at the banner's lower edge —
|
||||
acceptable for the rare historical-turn read path.
|
||||
*/
|
||||
.report-toc {
|
||||
position: sticky;
|
||||
top: 0.5rem;
|
||||
align-self: flex-end;
|
||||
margin-left: auto;
|
||||
position: fixed;
|
||||
top: 4rem;
|
||||
right: 1.25rem;
|
||||
z-index: 30;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.report-toc {
|
||||
right: calc(18rem + 1.25rem);
|
||||
}
|
||||
}
|
||||
.trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -221,6 +250,9 @@ in F8-09: that switch is available in the app-shell view menu.
|
||||
border-left-color: var(--color-accent);
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.report-toc {
|
||||
right: 0.75rem;
|
||||
}
|
||||
.surface {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
|
||||
Reference in New Issue
Block a user