fix(nav): prevent thrashing by using fixed position always

The thrashing was caused by a layout shift feedback loop: switching
between position sticky (takes up space) and position fixed (overlays)
caused content to shift, triggering IntersectionObserver again.

Fix: Always use position fixed so nav state changes are purely visual
(transparency, borders, colors) without any layout shifts.

Also removed unnecessary hysteresis code since the root cause is fixed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-10-20 14:13:47 -05:00
parent f81b88ae30
commit eff44b3ad1
2 changed files with 5 additions and 19 deletions

View File

@@ -580,7 +580,7 @@ export function AppNavBar({ variant = 'full', navSlot }: AppNavBarProps) {
shadow: isTransparent ? 'none' : 'sm',
borderBottom: isTransparent ? 'none' : '1px solid',
borderColor: isTransparent ? 'transparent' : 'gray.200',
position: isTransparent ? 'fixed' : 'sticky',
position: 'fixed',
top: 0,
left: 0,
right: 0,

View File

@@ -24,31 +24,17 @@ export function HeroAbacus() {
},
}
// Detect when hero scrolls out of view with hysteresis to prevent thrashing
// Detect when hero scrolls out of view
useEffect(() => {
if (!heroRef.current) return
let currentlyVisible = true // Start as visible (hero starts at top)
const observer = new IntersectionObserver(
([entry]) => {
// Use hysteresis: different thresholds for showing vs hiding
// When scrolling down (becoming invisible): hide when < 10% visible
// When scrolling up (becoming visible): show when > 30% visible
const ratio = entry.intersectionRatio
if (currentlyVisible && ratio < 0.1) {
// Was visible, now scrolled far enough to hide nav branding
currentlyVisible = false
setIsHeroVisible(false)
} else if (!currentlyVisible && ratio > 0.3) {
// Was hidden, now scrolled far enough to show nav branding
currentlyVisible = true
setIsHeroVisible(true)
}
// Hero is visible if more than 20% is in viewport
setIsHeroVisible(entry.intersectionRatio > 0.2)
},
{
threshold: [0, 0.1, 0.3, 0.5, 1],
threshold: [0, 0.2, 0.5, 1],
}
)