Fix two-column scroll layout: sticky sidebars clip and cannot scroll on all pages
Problem
Any page with a sticky right-column sidebar clips its content and prevents scrolling. The repro in the issue detail view is the most visible: the CLOSE action in the right column is cut off and unreachable by scrolling.
This is a systemic failure, not a one-off bug. 11 page stylesheets share the same broken pattern.
Root causes
1. body { zoom: 1.25 } distorts 100vh
_layout.scss:40 sets body { zoom: 1.25 }. The CSS 100vh unit is the layout viewport height before zoom is applied, so max-height: calc(100vh - ...) on any zoomed page calculates against the wrong height. A sidebar that should be 800 px tall is computed as 1000 px, overflowing the visible area.
Fix: replace 100vh with 100dvh (dynamic viewport height, which respects zoom and browser chrome) across all affected files.
2. Grid parents missing min-height: 0
CSS grid items stretch to their intrinsic content height by default. Without min-height: 0 on the grid container, a sticky child's max-height has no scroll boundary to work against — the child just expands to full content height and the scrollbar never appears.
Affected layout classes: .isd-layout, .isl-layout, .proposal-detail-layout, and several others.
3. Inconsistent sticky top offset
Pages use at least four different expressions for the sticky top value:
var(--header-height)(repo-home, explore)var(--sticky-offset, 80px)(proposal-detail, docs, blame, blob)var(--header-height) + var(--space-4)(issues list sidebar)- hardcoded
80px(fallback scattered through fallback values)
When the repo tab strip is present --sticky-offset accounts for both header + tab strip height. When it is absent, pages use --header-height alone. The inconsistency means sidebars on tabbed pages start too high and get obscured by the tab strip.
Fix: always use var(--sticky-offset, var(--header-height)) as the canonical expression.
4. Missing mobile reset on several sidebars
_issues.scss correctly resets the sidebar on mobile (@media (max-width: 900px) { position: static; max-height: none; overflow-y: visible; }). Several other pages (repo-home, explore, muse-docs) do not include this reset, causing the sticky/scroll behaviour to break on narrow viewports.
Affected files (11 pages)
| File | Issues |
|---|---|
pages/_issues.scss |
100vh in two sidebars (list + detail); --header-height inconsistency |
pages/_proposal-detail.scss |
100vh; grid parent lacks min-height: 0 |
pages/_commit-detail.scss |
height: 100vh on grid container clips both columns |
pages/_repo-home.scss |
100vh; no mobile reset |
pages/_explore.scss |
max-height: 100vh with no offset at all |
pages/_docs.scss |
height: calc(100vh - ...) |
pages/_muse-docs.scss |
100vh fallback |
pages/_blame.scss |
100vh |
pages/_blob.scss |
100vh |
pages/_profile.scss |
100vh in graph sidebar |
pages/_agents.scss |
overflow-y: auto with no height constraint |
_layout.scss |
Root cause: body { zoom: 1.25 } + no --sticky-offset default |
Canonical pattern (target state)
Every two-column page must use this exact shape:
// Grid wrapper — min-height: 0 is non-negotiable
.page-two-col {
display: grid;
grid-template-columns: 1fr 280px;
align-items: start;
min-height: 0; // ← lets grid children shrink and scroll
gap: var(--space-6);
@media (max-width: 900px) { grid-template-columns: 1fr; }
}
// Sticky scrolling sidebar
.page-sidebar {
position: sticky;
top: var(--sticky-offset, var(--header-height)); // ← canonical offset expression
max-height: calc(100dvh - var(--sticky-offset, var(--header-height)) - var(--space-4));
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--border-default) transparent;
// Mobile reset — required on every sidebar
@media (max-width: 900px) {
position: static;
max-height: none;
overflow-y: visible;
}
}
Key rules:
100dvhnot100vh— everywhere, no exceptionsmin-height: 0on every grid/flex parent of a scrolling columnvar(--sticky-offset, var(--header-height))as the canonical sticky top expression- Mobile reset on every sticky sidebar
Shared layout utilities (target state)
Rather than duplicating this pattern in 11 files, extract two utility classes into _layout.scss:
.layout-two-col // grid wrapper with min-height: 0 and responsive collapse
.layout-sidebar // sticky sidebar with correct dvh, offset, and mobile reset
Pages import the utilities and add only page-specific overrides (column widths, gap).
Test plan (TDD — write tests first)
Phase 1 — Visual regression baseline
Capture before screenshots of every affected page at 1440×900 (desktop) and 390×844 (mobile). These are the reference images for regression detection in Phase 3.
Phase 2 — Unit tests: layout invariants
Write CSS/DOM tests (Vitest + jsdom or Playwright component tests) asserting:
SIDEBAR_01—.layout-sidebarnever overflows its grid parent at any viewport widthSIDEBAR_02—.layout-sidebaris scrollable when its content exceedsmax-heightSIDEBAR_03—.layout-sidebarhasposition: staticatwidth < 900pxSIDEBAR_04— No element on any two-column page requires horizontal scrolling at 375px widthSIDEBAR_05— All sticky elements havetopequal to--sticky-offset(or--header-heighton pages without repo tabs)SIDEBAR_06— No100vhstring appears in compiled CSS output (must be100dvh)
Phase 3 — Integration: per-page scroll tests
Playwright tests for each of the 11 affected pages:
- Load the page at 1440×900
- Assert the sidebar is visible and its bottom edge is within the viewport
- Scroll the sidebar content to the bottom
- Assert the last child element of the sidebar is visible (not clipped)
- Repeat at 390×844 — sidebar must reflow to static block
Phase 4 — Regression: before/after visual diff
Run the visual regression suite from Phase 1 against the fixed build. All diffs must be intentional (layout changes) with no unintended colour, spacing, or typography regressions.
Acceptance criteria
- All 11 affected pages have a scrollable right sidebar that never clips content
- The CLOSE action on the issue detail right column is reachable by scrolling at any viewport width ≥ 375px
- No
100vhin compiled output — only100dvh min-height: 0present on every two-column grid parent.layout-two-coland.layout-sidebarutility classes extracted to_layout.scss- All 6 sidebar unit tests pass
- All 11 per-page scroll integration tests pass
- Mobile reflow (< 900px): all sidebars stack vertically with no scroll constraint
- No horizontal overflow at 375px on any page
Out of scope
- Redesigning the layout beyond the scroll/sticky fix
- Dark/light theme changes
- Any page not in the 11-file list above