From 218d94107d4f745c4de6b160c4887c5bc2bc7147 Mon Sep 17 00:00:00 2001 From: Andrei Date: Wed, 24 Sep 2025 20:31:34 +0000 Subject: [PATCH] Fix Bible reader page jumping and layout stability issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layout Stability Improvements: - Add consistent minHeight (60vh) to content containers to prevent layout shifts - Implement sticky navigation bar with smooth transitions - Position loading spinner absolutely to prevent content jumping - Add smooth opacity transitions during content loading Content Loading Enhancements: - Preserve previous verses during loading to prevent content flashing - Display previous verse count during transitions for visual continuity - Use requestAnimationFrame and setTimeout for smoother state transitions - Implement content fade-in effects with reduced opacity during loading Scroll Position Management: - Store scroll position before loading new content - Restore scroll position after content loads (maintains reading flow) - Smart scroll restoration that only applies when not navigating to specific verse - Prevent scroll jumping during chapter transitions User Experience Improvements: - Eliminate page "jumping" between chapter transitions - Remove container resizing issues that disrupted reading flow - Provide professional, book-like reading experience - Maintain visual continuity during all navigation actions - Smooth loading states with professional transitions Technical Implementation: - Add previousVerses state for content continuity - Implement position: sticky for navigation stability - Use CSS transitions for smooth visual effects - Optimize loading timing with requestAnimationFrame - Maintain consistent container dimensions throughout app lifecycle 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/[locale]/bible/reader.tsx | 88 ++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/app/[locale]/bible/reader.tsx b/app/[locale]/bible/reader.tsx index d9f65f2..4b8b409 100644 --- a/app/[locale]/bible/reader.tsx +++ b/app/[locale]/bible/reader.tsx @@ -147,6 +147,7 @@ export default function BibleReaderNew() { const [preferences, setPreferences] = useState(defaultPreferences) const [highlightedVerse, setHighlightedVerse] = useState(null) const [showScrollTop, setShowScrollTop] = useState(false) + const [previousVerses, setPreviousVerses] = useState([]) // Keep previous content during loading // Bookmark state const [isChapterBookmarked, setIsChapterBookmarked] = useState(false) @@ -387,11 +388,35 @@ export default function BibleReaderNew() { useEffect(() => { if (selectedBook && selectedChapter) { setLoading(true) + + // Store scroll position to prevent jumping + const scrollPosition = window.pageYOffset + fetch(`/api/bible/verses?bookId=${selectedBook}&chapter=${selectedChapter}`) .then(res => res.json()) .then(data => { - setVerses(data.verses || []) - setLoading(false) + const newVerses = data.verses || [] + + // Store previous verses before updating + setPreviousVerses(verses) + + // Use requestAnimationFrame to ensure smooth transition + requestAnimationFrame(() => { + setVerses(newVerses) + + // Small delay to allow content to render before removing loading state + setTimeout(() => { + setLoading(false) + setPreviousVerses([]) // Clear previous content after transition + + // Restore scroll position if we're not navigating to a specific verse + const urlVerse = new URLSearchParams(window.location.search).get('verse') + if (!urlVerse) { + // Maintain scroll position for better UX + window.scrollTo({ top: Math.min(scrollPosition, document.body.scrollHeight), behavior: 'auto' }) + } + }, 50) // Small delay for smoother transition + }) }) .catch(err => { console.error('Error fetching verses:', err) @@ -808,7 +833,11 @@ export default function BibleReaderNew() { p: preferences.readingMode ? 1 : 2, ...getThemeStyles(), border: preferences.readingMode ? 'none' : `1px solid ${getThemeStyles().borderColor}`, - backgroundColor: preferences.readingMode ? 'transparent' : getThemeStyles().backgroundColor + backgroundColor: preferences.readingMode ? 'transparent' : getThemeStyles().backgroundColor, + position: 'sticky', + top: 0, + zIndex: 1, + transition: 'all 0.2s ease' }} > {/* First Row: Navigation Filters */} @@ -1188,24 +1217,37 @@ export default function BibleReaderNew() { sx={{ maxWidth: preferences.columnLayout ? 'none' : '800px', mx: 'auto', - width: '100%' + width: '100%', + minHeight: '60vh', // Prevent layout shifts + position: 'relative' }} > - {loading ? ( - - - - ) : ( - + + {loading && ( + + + + )} + + {/* Chapter Header */} - {verses.length} {t('verses')} + {(loading && previousVerses.length > 0 ? previousVerses : verses).length} {t('verses')} {/* Verses */} - {verses.map(renderVerse)} + {(loading && previousVerses.length > 0 ? previousVerses : verses).map(renderVerse)} {/* Chapter Navigation */} @@ -1248,8 +1290,8 @@ export default function BibleReaderNew() { {t('nextChapter')} - - )} + +