From aab67d8375008cfd423d53ac3acf16a4106e8e1f Mon Sep 17 00:00:00 2001 From: Gigi Date: Mon, 13 Oct 2025 21:36:08 +0200 Subject: [PATCH] refactor(layout): switch to document scroll with sticky sidebars - Remove fixed container heights from three-pane layout - Desktop: sticky sidebars with max-height, document scrolls - Mobile: keep fixed overlays unchanged - Update scroll direction hook to use window scroll - Update progress indicator z-index to 1102 (above mobile overlays) - Apply Tailwind utilities to App container - Maintain responsive behavior across breakpoints --- src/App.tsx | 2 +- src/components/ThreePaneLayout.tsx | 5 +- src/styles/components/reader.css | 2 +- src/styles/layout/app.css | 75 +++++++++++++++++++----------- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 24c5bae2..ac590fd7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -238,7 +238,7 @@ function App() { -
+
diff --git a/src/components/ThreePaneLayout.tsx b/src/components/ThreePaneLayout.tsx index bb598226..28f281e9 100644 --- a/src/components/ThreePaneLayout.tsx +++ b/src/components/ThreePaneLayout.tsx @@ -98,11 +98,10 @@ const ThreePaneLayout: React.FC = (props) => { const mainPaneRef = useRef(null) // Detect scroll direction to hide/show mobile buttons - // On mobile, scroll happens in the main pane, not on window + // Now using window scroll (document scroll) instead of pane scroll const scrollDirection = useScrollDirection({ threshold: 10, - enabled: isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed, - elementRef: mainPaneRef + enabled: isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed }) const showMobileButtons = scrollDirection !== 'down' diff --git a/src/styles/components/reader.css b/src/styles/components/reader.css index d6cd0242..ab39a791 100644 --- a/src/styles/components/reader.css +++ b/src/styles/components/reader.css @@ -109,7 +109,7 @@ bottom: 0; left: 0; right: 0; - z-index: 1000; + z-index: 1102; /* Above mobile sidepanes (1001) and backdrop (999) */ background: rgba(26, 26, 26, 0.95); backdrop-filter: blur(8px); border-top: 1px solid rgba(255, 255, 255, 0.1); diff --git a/src/styles/layout/app.css b/src/styles/layout/app.css index 1f737d2f..5f9208f6 100644 --- a/src/styles/layout/app.css +++ b/src/styles/layout/app.css @@ -18,51 +18,64 @@ .two-pane.sidebar-collapsed { grid-template-columns: 60px 1fr; } -/* Three-pane layout */ +/* Three-pane layout - document scroll, sticky sidebars */ .three-pane { display: grid; grid-template-columns: var(--sidebar-width) 1fr var(--highlights-width); column-gap: 0; - height: calc(100vh - 2rem); transition: grid-template-columns 0.3s ease; position: relative; -} - -@supports (height: 100dvh) { - .three-pane { height: calc(100dvh - 2rem); } + min-height: 100vh; } .three-pane.sidebar-collapsed { grid-template-columns: var(--sidebar-collapsed-width) 1fr var(--highlights-width); } .three-pane.highlights-collapsed { grid-template-columns: var(--sidebar-width) 1fr var(--highlights-collapsed-width); } .three-pane.sidebar-collapsed.highlights-collapsed { grid-template-columns: var(--sidebar-collapsed-width) 1fr var(--highlights-collapsed-width); } -/* Mobile three-pane layout */ -@media (max-width: 768px) { - .three-pane { - grid-template-columns: 1fr; - grid-template-rows: 1fr; - height: 100vh; - height: 100dvh; +/* Desktop: sticky sidebars, document scroll */ +@media (min-width: 769px) { + .pane.sidebar { + position: sticky; + top: 1rem; + max-height: calc(100vh - 2rem); + overflow-y: auto; + align-self: start; + } + + .pane.main { + margin: 0 auto; + padding: 0 var(--main-horizontal-padding); + min-height: 100vh; + } + + .pane.highlights { + position: sticky; + top: 1rem; + max-height: calc(100vh - 2rem); + overflow-y: auto; + align-self: start; } - .three-pane.sidebar-collapsed, - .three-pane.highlights-collapsed, - .three-pane.sidebar-collapsed.highlights-collapsed { grid-template-columns: 1fr; } -} - -.pane.sidebar { overflow-y: auto; height: 100%; } -.pane.main { - overflow-y: auto; - height: 100%; - margin: 0 auto; - padding: 0 var(--main-horizontal-padding); - overflow-x: hidden; - contain: layout style; } /* Remove padding when sidebar is collapsed for zero gap */ .three-pane.sidebar-collapsed .pane.main { padding-left: 0; } .three-pane.sidebar-collapsed.highlights-collapsed .pane.main { padding-left: 0; } -.pane.highlights { overflow-y: auto; height: 100%; } + +/* Mobile three-pane layout */ +@media (max-width: 768px) { + .three-pane { + grid-template-columns: 1fr; + grid-template-rows: auto; + } + .three-pane.sidebar-collapsed, + .three-pane.highlights-collapsed, + .three-pane.sidebar-collapsed.highlights-collapsed { grid-template-columns: 1fr; } + + .pane.main { + margin: 0 auto; + padding: 0.5rem; + } +} /* Ensure panes are stacked in the correct order on desktop */ @media (min-width: 769px) { @@ -102,7 +115,13 @@ /* Highlights sidebar from right */ .pane.highlights { right: 0; transform: translateX(100%); } .pane.highlights.mobile-open { transform: translateX(0); box-shadow: -4px 0 12px rgba(0, 0, 0, 0.5); } - .pane.main { grid-column: 1; grid-row: 1; padding: 0.5rem; max-width: 100%; transition: opacity 0.2s ease; } + .pane.main { + grid-column: 1; + grid-row: 1; + padding: 0.5rem; + max-width: 100%; + transition: opacity 0.2s ease; + } /* Hide main content when sidepanes are open on mobile */ .three-pane .pane.main.mobile-hidden { opacity: 0; pointer-events: none; } .mobile-sidebar-backdrop {