From 48463e12ad77dc4cc993e4f33735c7e7ceb377c7 Mon Sep 17 00:00:00 2001 From: Giuseppina Crimi Date: Thu, 6 Aug 2026 14:08:46 +0200 Subject: [PATCH] fix: ToC toggle overlap and sticky title bleed-through - sticky ToC title lost its opaque background, letting scrolled headings show through behind it - ToC toggle position used window.innerWidth (includes the scrollbar) instead of document.documentElement.clientWidth, pushing it into the sidebar by the scrollbar's own width - .md-grid only got its extra clearance margin from 76.25em up, but the ToC toggle is offered from 60em, leaving it nowhere to sit at narrower widths where the ToC sidebar sat flush against the edge - .md-content__inner's 70ch min-width ignored the column width the sidebars already shrank it to, overflowing into the ToC sidebar --- .../assets/javascripts/toc-collapse-toggle.js | 18 +++++--- .../stylesheets/mkdocs-doubleslash-theme.css | 42 ++++++++++++++++--- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/mkdocs_doubleslash_theme/assets/javascripts/toc-collapse-toggle.js b/mkdocs_doubleslash_theme/assets/javascripts/toc-collapse-toggle.js index e76873f..cfc3bfe 100644 --- a/mkdocs_doubleslash_theme/assets/javascripts/toc-collapse-toggle.js +++ b/mkdocs_doubleslash_theme/assets/javascripts/toc-collapse-toggle.js @@ -158,12 +158,18 @@ } var width = wrapper.getBoundingClientRect().width; - // Extra 20px breathing room from the grid edge, but only when parked - // there in the collapsed state — flush against the ToC divider - // otherwise. Never closer than 20px to the real viewport edge either, - // or the wrapper ends up pinned to it on viewports with little margin. - var gap = collapsed ? 20 : 0; - var right = Math.max(20, window.innerWidth - anchor.getBoundingClientRect().right - width - gap); + // Extra 20px breathing room from the grid edge when parked there in + // the collapsed state; a smaller 8px gap otherwise so the button + // doesn't sit flush against the sidebar's scrollbar. Never closer + // than 20px to the real viewport edge either, or the wrapper ends up + // pinned to it on viewports with little margin. + var gap = collapsed ? 20 : 18; + // position:fixed's `right` is relative to the document's clientWidth + // (viewport minus scrollbar), not window.innerWidth (which includes + // it) — using innerWidth here pushed the button left by the + // scrollbar's own width, into the sidebar. + var viewportWidth = document.documentElement.clientWidth; + var right = Math.max(20, viewportWidth - anchor.getBoundingClientRect().right - width - gap); wrapper.style.right = right + "px"; } diff --git a/mkdocs_doubleslash_theme/assets/stylesheets/mkdocs-doubleslash-theme.css b/mkdocs_doubleslash_theme/assets/stylesheets/mkdocs-doubleslash-theme.css index 0c7e0d9..72481d0 100644 --- a/mkdocs_doubleslash_theme/assets/stylesheets/mkdocs-doubleslash-theme.css +++ b/mkdocs_doubleslash_theme/assets/stylesheets/mkdocs-doubleslash-theme.css @@ -31,6 +31,12 @@ --ds-motion-duration: 0.3s; --ds-motion-transition: var(--ds-motion-duration) ease all; --ds-wide-max-width: min(1800px, 100% - 4rem); + /* Separate from --ds-wide-max-width (used for the actual reading column + fill-width): this is only for .md-main__inner/.md-grid's own margin, + where 8rem (4rem per side) leaves enough room for the sidebar-collapse + toggle buttons (43px + 20px gap ≈ 63px each) to sit outside it without + drifting off-screen at narrower viewports. */ + --ds-grid-max-width: min(1800px, 100% - 8rem); /* Typography tokens — source: frontend/DESIGN.md (living-styleguide, ref=main) */ --ds-typography-body-website-font-size: 18px; @@ -63,6 +69,14 @@ --ds-typography-heading-h6-margin-bottom: 12px; } +/* Material scales the root font-size in three hard jumps (125% / 137.5% / + 150% at 100em / 125em) — every rem-based size and spacing on the page + snaps at those breakpoints. clamp() scales it continuously instead: 20px + up to ~1600px, 24px from ~2560px, linear in between. */ +html { + font-size: clamp(20px, 16px + 0.3125vw, 24px); +} + [data-md-color-scheme="default"] { --md-primary-fg-color: var(--ds-color-on-primary); --md-primary-bg-color: var(--ds-color-primary); @@ -244,6 +258,15 @@ sidebar divider instead of floating centered in the available space. */ .md-content__inner { max-width: 90ch; + /* Capped at the column's own available width too: .md-content__inner + isn't itself a flex item — it's a plain block inside the + .md-content column that the sidebars already shrank — so an + unqualified 70ch floor ignores that shrunk width and overflows + into the ToC/nav sidebar whenever both are open on a narrower + viewport. min() keeps the floor from exceeding it; the 2.4rem + subtracted is this element's own left+right margin (1.2rem each), + which sits outside the border-box that min-width sizes. */ + min-width: min(70ch, calc(100% - 2.4rem)); transition: max-width var(--ds-motion-duration) ease; } @@ -413,16 +436,22 @@ width: 1rem; } +/* The ToC toggle is offered starting at 60em (below), narrower than the + 76.25em block below — without room here too, it has nowhere to sit but + on top of the ToC sidebar, which is flush against the viewport edge at + these widths. */ +@media screen and (min-width: 60em) { + .md-grid { + max-width: var(--ds-grid-max-width); + } +} + /* Only constrain width on wide desktop layouts (>= 76.25em) — unguarded, the "100% - 4rem" term shrinks these below their natural width on ANY viewport, adding unwanted side margins on mobile too. Content only picks up the same width as .md-grid once a sidebar is actually collapsed, so it stays aligned with the header instead of fighting the 90ch reading cap. */ @media screen and (min-width: 76.25em) { - .md-grid { - max-width: var(--ds-wide-max-width); - } - /* Once a sidebar is actually collapsed, drop the 90ch reading cap so text fills the reclaimed space instead of leaving it padded out on one side — that's the whole point of the widen toggle. Material's own @@ -733,8 +762,9 @@ } } -/* Remove the opaque background/box-shadow Material adds to the sticky ToC title */ +/* Remove Material's box-shadow on the sticky ToC title, but keep an opaque + background — otherwise scrolled-past headings show through behind it. */ .md-nav--secondary .md-nav__title { - background-color: transparent; + background-color: var(--md-default-bg-color); box-shadow: none; } \ No newline at end of file