Skip to content

Commit e8ae80f

Browse files
committed
fix: sticky package header offset
1 parent 9569132 commit e8ae80f

3 files changed

Lines changed: 21 additions & 20 deletions

File tree

app/components/ReadmeTocDropdown.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function close() {
9999
}
100100
101101
function select(id: string) {
102-
scrollToAnchor(id, props.scrollToHeading)
102+
scrollToAnchor(id, { scrollFn: props.scrollToHeading })
103103
close()
104104
triggerRef.value?.focus()
105105
}

app/composables/useActiveTocItem.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { TocItem } from '#shared/types/readme'
22
import type { Ref } from 'vue'
3+
import { scrollToAnchor } from '~/utils/scrollToAnchor'
34

45
/**
56
* Composable for tracking the currently visible heading in a TOC.
@@ -93,8 +94,7 @@ export function useActiveTocItem(toc: Ref<TocItem[]>) {
9394

9495
// Scroll to a heading with observer disconnection during scroll
9596
const scrollToHeading = (id: string) => {
96-
const element = document.getElementById(id)
97-
if (!element) return
97+
if (!document.getElementById(id)) return
9898

9999
// Clean up any previous scroll monitoring
100100
if (scrollCleanup) {
@@ -110,19 +110,8 @@ export function useActiveTocItem(toc: Ref<TocItem[]>) {
110110
observer.disconnect()
111111
}
112112

113-
// Calculate scroll position with header offset (5rem = 80px)
114-
// This matches the scroll-padding-top in main.css
115-
const HEADER_OFFSET = 80
116-
const elementTop = element.getBoundingClientRect().top + window.scrollY
117-
const targetScrollY = elementTop - HEADER_OFFSET
118-
119-
// Use scrollTo for precise control (respects CSS scroll-behavior: smooth)
120-
// Don't update URL hash yet - do it after scroll completes to avoid
121-
// browser native scroll-to-anchor behavior interfering
122-
window.scrollTo({
123-
top: targetScrollY,
124-
behavior: 'smooth',
125-
})
113+
// Scroll, but do not update url until scroll ends
114+
scrollToAnchor(id, { updateUrl: false })
126115

127116
const handleScrollEnd = () => {
128117
history.replaceState(null, '', `#${id}`)

app/utils/scrollToAnchor.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1+
export interface ScrollToAnchorOptions {
2+
/** Custom scroll function (e.g., from useActiveTocItem) */
3+
scrollFn?: (id: string) => void
4+
/** Whether to update the URL hash (default: true) */
5+
updateUrl?: boolean
6+
}
7+
18
/**
29
* Scroll to an element by ID, using a custom scroll function if provided,
310
* otherwise falling back to default scroll behavior with header offset.
411
*
512
* @param id - The element ID to scroll to
6-
* @param scrollFn - Optional custom scroll function (e.g., from useActiveTocItem)
13+
* @param options - Optional configuration for scroll behavior
714
*/
8-
export function scrollToAnchor(id: string, scrollFn?: (id: string) => void): void {
15+
export function scrollToAnchor(id: string, options?: ScrollToAnchorOptions): void {
16+
const { scrollFn, updateUrl = true } = options ?? {}
17+
918
// Use custom scroll function if provided
1019
if (scrollFn) {
1120
scrollFn(id)
@@ -18,8 +27,9 @@ export function scrollToAnchor(id: string, scrollFn?: (id: string) => void): voi
1827

1928
// Calculate scroll position with header offset (matches scroll-padding-top in main.css)
2029
const HEADER_OFFSET = 80
30+
const PKG_STICKY_HEADER_OFFSET = 52
2131
const elementTop = element.getBoundingClientRect().top + window.scrollY
22-
const targetScrollY = elementTop - HEADER_OFFSET
32+
const targetScrollY = elementTop - (HEADER_OFFSET + PKG_STICKY_HEADER_OFFSET)
2333

2434
// Use scrollTo for precise control
2535
window.scrollTo({
@@ -29,5 +39,7 @@ export function scrollToAnchor(id: string, scrollFn?: (id: string) => void): voi
2939

3040
// Update URL hash after initiating scroll
3141
// Use replaceState to avoid triggering native scroll-to-anchor behavior
32-
history.replaceState(null, '', `#${id}`)
42+
if (updateUrl) {
43+
history.replaceState(null, '', `#${id}`)
44+
}
3345
}

0 commit comments

Comments
 (0)