Skip to content

fix(css): adjust hash link scroll margin for sticky breadcrumbs (#121) - #209

Merged
luandro merged 4 commits into
mainfrom
fix/121-hash-link-scroll-margin
Sep 12, 2026
Merged

fix(css): adjust hash link scroll margin for sticky breadcrumbs (#121)#209
luandro merged 4 commits into
mainfrom
fix/121-hash-link-scroll-margin

Conversation

@luandro

@luandro luandro commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #121.

Problem

When clicking anchor links (TOC entries, headings, or footnotes) on documentation pages, the viewport scrolls past the heading text, obscuring it behind the sticky breadcrumbs bar (.theme-doc-breadcrumbs). This happens because Docusaurus default .anchor scroll-margin only accounts for the navbar height (var(--ifm-navbar-height) + 0.5rem), ignoring the sticky breadcrumbs bar sitting below it (top: navbar + 2px, ~45px tall).

Solution

Added scroll-margin-top offset for :is(.theme-doc-markdown, .markdown) :target and .anchor in src/css/custom.css calculating:
calc(var(--ifm-navbar-height, 60px) + 2px + 45px + 0.75rem)

  • var(--ifm-navbar-height, 60px) + 2px: navbar height plus offset matching sticky breadcrumbs top
  • + 45px: breadcrumb bar height
  • + 0.75rem: breathing room above the target element

Scoped to doc content, covering headings via .anchor and footnotes/targets via :target.

Greptile Summary

Adds dynamic hash-link scroll offsets for documentation pages with sticky breadcrumbs.

  • Measures actual breadcrumb height with ResizeObserver and exposes it through a CSS custom property.
  • Applies the combined navbar and breadcrumb offset to headings, footnotes, and other document targets.
  • Limits initial hash realignment to one attempt per route update, preserving manual scroll position during later resizes.

Confidence Score: 5/5

The PR appears safe to merge; both previous findings are fully addressed and no new actionable defect was established.

Dynamic breadcrumb measurement handles wrapped breadcrumbs, while the new one-shot guard prevents later resize events from overriding the user's manual scroll position.

Important Files Changed

Filename Overview
src/client/scroll-to-top.ts Measures breadcrumb height and performs at most one initial hash-target realignment per route update.
src/css/custom.css Uses the measured breadcrumb height in document target scroll margins.

Reviews (4): Last reviewed commit: "fix(client): avoid overriding user scrol..." | Re-trigger Greptile

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@luandro

luandro commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

@greptile review

@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

🚀 Preview Deployment

Your documentation preview is ready!

Preview URL: https://pr-209.comapeo-docs-82j.pages.dev

📦 Content: from content branch (same source as staging/production)

This preview will update automatically when you push new commits to this PR.


Built with commit d18a6d1

Comment thread src/css/custom.css Outdated
:is(.theme-doc-markdown, .markdown) :target,
:is(.theme-doc-markdown, .markdown) .anchor {
scroll-margin-top: calc(
var(--ifm-navbar-height, 60px) + 2px + 45px + 0.75rem

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Fixed Height Misses Wrapping

The offset assumes the breadcrumb bar is always 45px tall, but its height is content-driven. When breadcrumbs wrap on a narrow screen or use longer localized labels, the bar becomes taller while this offset stays fixed. Hash navigation can therefore still place headings or footnotes behind the sticky breadcrumb. Derive the offset from the actual height or account for wrapped breadcrumbs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/css/custom.css
Line: 484

Comment:
**Fixed Height Misses Wrapping**

The offset assumes the breadcrumb bar is always 45px tall, but its height is content-driven. When breadcrumbs wrap on a narrow screen or use longer localized labels, the bar becomes taller while this offset stays fixed. Hash navigation can therefore still place headings or footnotes behind the sticky breadcrumb. Derive the offset from the actual height or account for wrapped breadcrumbs.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@luandro

luandro commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

@greptile review

Addressed: Replaced the fixed 45px offset with a dynamic CSS variable --doc-breadcrumbs-height in src/css/custom.css. Integrated a ResizeObserver on .theme-doc-breadcrumbs in src/client/scroll-to-top.ts to dynamically measure the rendered border-box height on mount and resize (handling multi-line wrapping and localized breadcrumb lengths), updating --doc-breadcrumbs-height in real-time, resetting to 0px when breadcrumbs are absent, and re-aligning hash scroll targets upon initial measurement.

@luandro

luandro commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

@greptile review

Addressed: Wired realignHashTarget into setHeight, triggered on both initial breadcrumbs render and every dynamic ResizeObserver resize event whenever the measured height changes. This guarantees that if breadcrumbs grow or wrap post-load (due to narrow viewports or dynamic localized labels), any active hash target is immediately re-aligned so it never remains obscured.

Comment thread src/client/scroll-to-top.ts Outdated
`${rounded}px`
);
// Re-align hash target when breadcrumbs render or resize
realignHashTarget();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Resize Restores Stale Target

When the breadcrumb resizes after the user has manually scrolled away from the current hash target, setHeight calls scrollIntoView() because the hash remains in the URL. Viewport resizing, font loading, zooming, or breadcrumb wrapping can therefore jump the user back to an earlier target and lose their chosen scroll position. Limit realignment to initial hash navigation or otherwise avoid overriding later user scrolling.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/client/scroll-to-top.ts
Line: 138

Comment:
**Resize Restores Stale Target**

When the breadcrumb resizes after the user has manually scrolled away from the current hash target, `setHeight` calls `scrollIntoView()` because the hash remains in the URL. Viewport resizing, font loading, zooming, or breadcrumb wrapping can therefore jump the user back to an earlier target and lose their chosen scroll position. Limit realignment to initial hash navigation or otherwise avoid overriding later user scrolling.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@luandro

luandro commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

@greptile review

Addressed: Added hasAlignedInitialHash guard in src/client/scroll-to-top.ts to ensure hash target realignment only happens once on initial navigation/measurement when deep-linked, and never runs on subsequent breadcrumbs resizes. This preserves the user's manual scroll position if the viewport resizes, fonts load, or zoom levels change.

@luandro
luandro merged commit 85e1e59 into main Sep 12, 2026
4 checks passed
@luandro
luandro deleted the fix/121-hash-link-scroll-margin branch September 12, 2026 01:18
@github-actions

Copy link
Copy Markdown
Contributor

🧹 Preview Deployment Cleanup

The preview deployment for this PR has been cleaned up.

Preview URL was: https://pr-209.comapeo-docs.pages.dev


Note: Cloudflare Pages deployments follow automatic retention policies. Old previews are cleaned up automatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

links to hashes (H2 & H3) jump to disorienting position

1 participant