/** * Composable for home page hero scroll behavior * Starts with full-screen hero, collapses to fixed header on scroll */ export const useHeroScroll = () => { const scrollY = ref(0) // Hero height = viewport height minus some space (e.g., 80px for visual balance) const heroBaseHeight = ref(0) const collapsedHeight = 100 // Fixed header height when collapsed // Calculate hero height based on viewport const updateHeroHeight = () => { if (import.meta.client) { heroBaseHeight.value = window.innerHeight - 80 // Nearly full screen } } // How much to collapse (0 = full hero, 1 = fully collapsed) const collapseProgress = computed(() => { const maxScroll = heroBaseHeight.value - collapsedHeight if (maxScroll <= 0) return 1 return Math.min(1, Math.max(0, scrollY.value / maxScroll)) }) // Current hero height (animated) const heroHeight = computed(() => { const maxScroll = heroBaseHeight.value - collapsedHeight return Math.max(collapsedHeight, heroBaseHeight.value - scrollY.value) }) // Is fully collapsed to fixed header? const isCollapsed = computed(() => collapseProgress.value >= 1) // Padding for content below hero const contentPaddingTop = computed(() => { return heroHeight.value }) const onScroll = () => { if (import.meta.client) { scrollY.value = window.scrollY } } const onResize = () => { updateHeroHeight() } onMounted(() => { if (import.meta.client) { updateHeroHeight() window.addEventListener('scroll', onScroll, { passive: true }) window.addEventListener('resize', onResize, { passive: true }) onScroll() } }) onUnmounted(() => { if (import.meta.client) { window.removeEventListener('scroll', onScroll) window.removeEventListener('resize', onResize) } }) return { scrollY: readonly(scrollY), heroBaseHeight: readonly(heroBaseHeight), heroHeight, collapseProgress, isCollapsed, contentPaddingTop, collapsedHeight } }