Overlay scrollbars via OverlayScrollbars

overflow: overlay is dead in current Chromium, so an appearing native
scrollbar shifted the layout again. OverlayScrollbars replaces it with
floating auto-hiding scrollbars that never reserve space (native window
scroll APIs unaffected on a body target). Themed via --os-* variables in
pagerite.css; the html scrollbar-color styling stays as no-JS fallback.
(package-lock.json is gitignored.)
This commit is contained in:
2026-08-18 20:30:08 +00:00
parent 7f140001fa
commit a75ad6f5da
4 changed files with 29 additions and 21 deletions
+4
View File
@@ -119,6 +119,10 @@ not for the public pages. See `docs/design-principles.md` for the design.
- `frontend/src/` — the Vue editor and public-page entries. - `frontend/src/` — the Vue editor and public-page entries.
- `main.js` — Vue editor app entry, mounts PageEditor/SiteEditor. - `main.js` — Vue editor app entry, mounts PageEditor/SiteEditor.
- `pagerite.js` — public page entry; runs fetch-navigation, scroll-reveal, - `pagerite.js` — public page entry; runs fetch-navigation, scroll-reveal,
OverlayScrollbars on `document.body` (floating, auto-hiding scrollbars
that never reserve layout space or shift the page when appearing;
native scroll APIs like `window.scrollTo` keep working; themed via the
`--os-*` variables in pagerite.css),
brand shrink-to-fit (the themed size is the maximum; JS reduces the brand shrink-to-fit (the themed size is the maximum; JS reduces the
font-size so a long brand or narrow viewport still fits one line), font-size so a long brand or narrow viewport still fits one line),
code copy buttons, and the auth check. It first probes `GET /auth/api/settings` code copy buttons, and the auth check. It first probes `GET /auth/api/settings`
+1
View File
@@ -18,6 +18,7 @@
"@codemirror/view": "^6.43.8", "@codemirror/view": "^6.43.8",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"overlayscrollbars": "^2.16.0",
"paskia": "file:../../paskia/paskia-js", "paskia": "file:../../paskia/paskia-js",
"transliteration": "^2.6.1", "transliteration": "^2.6.1",
"vue": "^3.5.26", "vue": "^3.5.26",
+12 -21
View File
@@ -67,31 +67,22 @@
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
/* Overlay scrollbars where supported (Chromium/WebKit, deprecated but /* Native-scrollbar fallback styling (JS off or before pagerite.js runs):
working): a scrollbar that appears covers a strip of the layout instead thin, theme-muted thumb on a transparent track. With JS the scrollbars
of shifting it, and no space is reserved while absent — so 100vw always are replaced by OverlayScrollbars (see pagerite.js) — floating,
equals the body width and .wide breakout margins stay exact. Ignored by auto-hidden scrollbars styled by the --os-* variables below, so they
Firefox (unknown value), which keeps its thin classic scrollbar. */ never reserve layout space or shift the page when appearing. */
overflow-y: overlay;
/* Lightweight scrollbar styling: thin, theme-muted thumb on a transparent
track where supported (Firefox: the two properties; WebKit: the rules
below). Full restyling deliberately not attempted. */
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: color-mix(in srgb, var(--muted) 45%, transparent) transparent; scrollbar-color: color-mix(in srgb, var(--muted) 45%, transparent) transparent;
} }
::-webkit-scrollbar { .os-scrollbar {
width: 0.5rem; --os-size: 0.5rem;
height: 0.5rem; --os-thumb-bg: color-mix(in srgb, var(--muted) 45%, transparent);
} --os-thumb-hover-bg: color-mix(in srgb, var(--muted) 65%, transparent);
--os-thumb-active-bg: var(--muted);
::-webkit-scrollbar-thumb { --os-track-bg: transparent;
background: color-mix(in srgb, var(--muted) 45%, transparent); --os-thumb-border-radius: 0.25rem;
border-radius: 0.25rem;
}
::-webkit-scrollbar-track {
background: transparent;
} }
body { body {
+12
View File
@@ -5,8 +5,20 @@
// Also: scroll-reveal effects and code copy buttons. These need no // Also: scroll-reveal effects and code copy buttons. These need no
// support from the article itself and are re-applied after each swap. // support from the article itself and are re-applied after each swap.
import { showAuthIframe } from 'paskia' import { showAuthIframe } from 'paskia'
import { OverlayScrollbars } from "overlayscrollbars";
import "overlayscrollbars/overlayscrollbars.css";
(() => { (() => {
// Overlay scrollbars: the native kind reserves a strip of layout (or
// shifts the layout when it appears; overflow: overlay is dead), so we
// replace it with floating ones that cover content instead. The body
// remains the native viewport scroller — window scroll events, scrollY
// and scroll restoration are unaffected; only the scrollbar UI is
// custom. Theme variables are in pagerite.css.
OverlayScrollbars(document.body, {
scrollbars: { autoHide: "scroll" },
});
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
// In dev the base stylesheet is injected by Vite from JS (linking the // In dev the base stylesheet is injected by Vite from JS (linking the
// raw module would pull in its HMR wrapper). Theme and banner-design // raw module would pull in its HMR wrapper). Theme and banner-design