From d88b3cea8ea3845b45f3bf2d499ca37e5d2e42b4 Mon Sep 17 00:00:00 2001 From: retoor Date: Tue, 8 Sep 2026 20:48:54 +0200 Subject: [PATCH] Make sticky sidebar/rail columns independently scrollable Every sidebar/rail column across the app (feed's left filter panel and right rail, post detail's left/right rails, profile's sidebar, the leaderboard's two rails, the dashboard sidebar, and the DeepSearch citations pane) is `position: sticky` with no max-height or overflow of its own. When a column's content is taller than the viewport, it doesn't scroll with the page while pinned - it renders past the bottom of the screen, unreachable until it "un-sticks" and flies past during a fast page scroll. Confirmed live: the docs nav sidebar alone is ~4300px of links against a ~900px viewport. Each sticky column now gets a max-height matched to its own top offset, `overflow-y: auto`, `overscroll-behavior: contain`, and a hidden scrollbar (scrollbar-width/-ms-overflow-style/::-webkit- scrollbar), the same invisible-scroll recipe already used by the messages page. Purely additive CSS - no template changes, since every target already had a distinguishing class. Fixing the shared `.sidebar-card` component in sidebar.css covers feed, admin, docs, gists, projects, battles, quizzes, and every /tools/* page in one place. Also caught and fixed a latent bug along the way: post.css already neutralized nested `.sidebar-card`s inside `.post-page-sidebar` (position: static) to stop them independently double-sticking, but the identical setup on the quiz page (`.feed-right` wrapping the scoreboard's two `.sidebar-card`s) never got that override - added the matching `.feed-right .sidebar-card` rule. Verified live with Playwright at a squeezed viewport height on feed, post, profile, leaderboard, docs, and the quiz scoreboard: every column's scrollTop reaches its true end independently, the center column never moves, and no scrollbar is visible in any screenshot. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Vnp7vzE4hvsytMo5YjszJm --- devplacepy/static/css/deepsearch.css | 8 ++++++++ devplacepy/static/css/feed.css | 26 +++++++++++++++++++++++++ devplacepy/static/css/landing.css | 10 ++++++++++ devplacepy/static/css/post.css | 12 ++++++++++++ devplacepy/static/css/profile.css | 10 ++++++++++ devplacepy/static/css/project_files.css | 8 ++++++++ devplacepy/static/css/sidebar.css | 10 ++++++++++ pyproject.toml | 2 +- 8 files changed, 85 insertions(+), 1 deletion(-) diff --git a/devplacepy/static/css/deepsearch.css b/devplacepy/static/css/deepsearch.css index 348de7c..e3807b4 100644 --- a/devplacepy/static/css/deepsearch.css +++ b/devplacepy/static/css/deepsearch.css @@ -545,6 +545,9 @@ dp-deepsearch-chat { .ds-chat-log { flex: 1; overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; padding: var(--space-md); display: flex; flex-direction: column; @@ -552,6 +555,11 @@ dp-deepsearch-chat { min-height: 240px; } +.ds-chat-log::-webkit-scrollbar { + width: 0; + height: 0; +} + .ds-chat-msg { display: flex; } diff --git a/devplacepy/static/css/feed.css b/devplacepy/static/css/feed.css index f9b3b6e..e554bb7 100644 --- a/devplacepy/static/css/feed.css +++ b/devplacepy/static/css/feed.css @@ -221,11 +221,27 @@ .feed-right { position: sticky; top: calc(var(--nav-height) + 1rem); + max-height: calc(100vh - var(--nav-height) - 2rem); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; display: flex; flex-direction: column; gap: 1rem; } +.feed-right::-webkit-scrollbar { + width: 0; + height: 0; +} + +.feed-right .sidebar-card { + position: static; + max-height: none; + overflow: visible; +} + .daily-topic-card { background: var(--bg-card); border: 1px solid var(--border); @@ -493,6 +509,16 @@ .leaderboard-page > aside { position: sticky; top: calc(var(--nav-height) + 1rem); + max-height: calc(100vh - var(--nav-height) - 2rem); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.leaderboard-page > aside::-webkit-scrollbar { + width: 0; + height: 0; } .leaderboard-main { diff --git a/devplacepy/static/css/landing.css b/devplacepy/static/css/landing.css index 88178aa..05a671e 100644 --- a/devplacepy/static/css/landing.css +++ b/devplacepy/static/css/landing.css @@ -272,6 +272,16 @@ gap: 1rem; position: sticky; top: 5rem; + max-height: calc(100vh - 5rem - 1rem); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.dashboard-sidebar::-webkit-scrollbar { + width: 0; + height: 0; } .dashboard-sidebar-card { diff --git a/devplacepy/static/css/post.css b/devplacepy/static/css/post.css index 1fb89fa..723a262 100644 --- a/devplacepy/static/css/post.css +++ b/devplacepy/static/css/post.css @@ -13,10 +13,22 @@ gap: 1rem; position: sticky; top: calc(var(--nav-height) + 1rem); + max-height: calc(100vh - var(--nav-height) - 2rem); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.post-page-sidebar::-webkit-scrollbar { + width: 0; + height: 0; } .post-page-sidebar .sidebar-card { position: static; + max-height: none; + overflow: visible; } .post-page { diff --git a/devplacepy/static/css/profile.css b/devplacepy/static/css/profile.css index 13c947a..9071a72 100644 --- a/devplacepy/static/css/profile.css +++ b/devplacepy/static/css/profile.css @@ -10,9 +10,19 @@ .profile-sidebar { position: sticky; top: calc(var(--nav-height) + 1rem); + max-height: calc(100vh - var(--nav-height) - 2rem); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; min-width: 0; } +.profile-sidebar::-webkit-scrollbar { + width: 0; + height: 0; +} + .profile-card { background: var(--bg-card); border: 1px solid var(--border); diff --git a/devplacepy/static/css/project_files.css b/devplacepy/static/css/project_files.css index fc25df9..710157d 100644 --- a/devplacepy/static/css/project_files.css +++ b/devplacepy/static/css/project_files.css @@ -53,11 +53,19 @@ width: 280px; flex-shrink: 0; overflow: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; padding: 0.5rem; border-right: 1px solid var(--border); background: var(--bg-card-hover); } +.pf-tree::-webkit-scrollbar { + width: 0; + height: 0; +} + .pf-tree-empty { padding: 1rem; font-size: 0.8125rem; diff --git a/devplacepy/static/css/sidebar.css b/devplacepy/static/css/sidebar.css index fd90320..6a7f526 100644 --- a/devplacepy/static/css/sidebar.css +++ b/devplacepy/static/css/sidebar.css @@ -3,6 +3,11 @@ .sidebar-card { position: sticky; top: calc(var(--nav-height) + 1rem); + max-height: calc(100vh - var(--nav-height) - 2rem); + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-width: none; + -ms-overflow-style: none; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); @@ -10,6 +15,11 @@ padding: 1rem; } +.sidebar-card::-webkit-scrollbar { + width: 0; + height: 0; +} + .sidebar-card .sidebar-heading { font-size: 0.75rem; font-weight: 700; diff --git a/pyproject.toml b/pyproject.toml index 89d3d8d..f011dd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "devplacepy" -version = "1.0.10" +version = "1.0.11" description = "DevPlace - The Developer Social Network" requires-python = ">=3.12" dependencies = [