docs: keep banner height in sync via ResizeObserver#112
Conversation
The height CSS variable was set once in a single rAF and never updated afterward. On window resize, orientation change, or text reflow the banner height could change, leaving the VitePress nav offset stale and causing overlap or a visible gap. Observe the banner element and resync the variable whenever its size changes; disconnect the observer when the banner is dismissed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #112 +/- ##
=======================================
Coverage 94.08% 94.08%
=======================================
Files 26 26
Lines 4055 4055
Branches 4055 4055
=======================================
Hits 3815 3815
Misses 155 155
Partials 85 85 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Greptile SummaryReplaces the one-shot Confidence Score: 5/5Safe to merge — the fix is correct, the observer lifecycle is properly managed, and the only finding is a minor P2 optimization. No P0 or P1 issues. The logic is sound: observer is set up after the element is in the DOM, disconnected on dismiss, and guarded against environments without ResizeObserver. The sole comment is a P2 suggestion to avoid a forced reflow inside the callback by using the entry's borderBoxSize. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant initBanner
participant render
participant DOM
participant rAF
participant ResizeObserver
participant CSSVar as --vp-layout-top-height
initBanner->>render: fetch() resolves, banner not dismissed
render->>DOM: document.body.prepend(el)
render->>rAF: requestAnimationFrame(syncHeight)
render->>ResizeObserver: observer.observe(el)
rAF-->>CSSVar: syncHeight() → set height (initial)
ResizeObserver-->>CSSVar: syncHeight() on first observation
Note over ResizeObserver,CSSVar: Re-fires on window resize / text reflow
ResizeObserver-->>CSSVar: syncHeight() → update height
alt User clicks Dismiss
DOM->>ResizeObserver: observer.disconnect()
DOM->>CSSVar: removeProperty(--vp-layout-top-height)
DOM->>DOM: el.remove()
end
Reviews (1): Last reviewed commit: "docs: keep --vp-layout-top-height in syn..." | Re-trigger Greptile |
| const syncHeight = () => { | ||
| document.documentElement.style.setProperty( | ||
| "--vp-layout-top-height", | ||
| `${el.offsetHeight}px`, | ||
| ); |
There was a problem hiding this comment.
Forced reflow in ResizeObserver callback
el.offsetHeight triggers a synchronous layout calculation every time the observer fires. Inside a ResizeObserver callback the browser already provides the new dimensions via entries, so you can read them without forcing an extra reflow.
| const syncHeight = () => { | |
| document.documentElement.style.setProperty( | |
| "--vp-layout-top-height", | |
| `${el.offsetHeight}px`, | |
| ); | |
| const syncHeight = (entries?: ResizeObserverEntry[]) => { | |
| const height = | |
| entries?.[0]?.borderBoxSize?.[0]?.blockSize ?? el.offsetHeight; | |
| document.documentElement.style.setProperty( | |
| "--vp-layout-top-height", | |
| `${height}px`, | |
| ); | |
| }; |
The borderBoxSize path is what you actually want (border-box matches offsetHeight semantics) and is available in all browsers that support ResizeObserver. The ?? el.offsetHeight fallback keeps the initial requestAnimationFrame call working when called without entries.
There was a problem hiding this comment.
Code Review
This pull request implements dynamic height synchronization for the banner component using a ResizeObserver. The review feedback identifies opportunities to prevent a race condition when the banner is dismissed, improve measurement precision using getBoundingClientRect, and eliminate a redundant requestAnimationFrame call when ResizeObserver is supported.
| const syncHeight = () => { | ||
| document.documentElement.style.setProperty( | ||
| "--vp-layout-top-height", | ||
| `${el.offsetHeight}px`, | ||
| ); | ||
| }; |
There was a problem hiding this comment.
There are two improvements for the syncHeight function:
- Race Condition: Since
syncHeightis called viarequestAnimationFrame, it might execute after the banner has been dismissed and removed from the DOM. In this case,el.offsetHeightwill be0, and the code will re-add the--vp-layout-top-heightproperty as0pxeven though it was explicitly removed in the click handler. Adding anel.isConnectedcheck prevents this. - Precision:
el.offsetHeightreturns an integer. For layout-sensitive variables like this, usingel.getBoundingClientRect().heightis preferred as it provides sub-pixel precision, avoiding potential 1px gaps or overlaps on high-DPI screens.
| const syncHeight = () => { | |
| document.documentElement.style.setProperty( | |
| "--vp-layout-top-height", | |
| `${el.offsetHeight}px`, | |
| ); | |
| }; | |
| const syncHeight = () => { | |
| if (!el.isConnected) return; | |
| const height = el.getBoundingClientRect().height; | |
| document.documentElement.style.setProperty( | |
| "--vp-layout-top-height", | |
| height + "px", | |
| ); | |
| }; |
| requestAnimationFrame(syncHeight); | ||
| observer?.observe(el); |
There was a problem hiding this comment.
When ResizeObserver is supported, observer.observe(el) will trigger an initial notification, making the requestAnimationFrame(syncHeight) call redundant. You can wrap the rAF in an else block to only use it as a fallback for older browsers.
if (observer) {
observer.observe(el);
} else {
requestAnimationFrame(syncHeight);
}A small patch release that fixes a panic when generating notes against releases with multi-byte characters in their bodies, and picks up a security fix in `rustls-webpki`. ## Fixed - **Don't panic on multi-byte chars in style-reference bodies** — `communique generate` truncates each recent release body to 3072 bytes to keep the prompt small, but previously sliced `&body[..3072]` directly. If byte 3072 fell inside a multi-byte UTF-8 character (common with em-dashes, which are 3 bytes), the command would panic with `byte index 3072 is not a char boundary`. The truncation now walks back to the nearest char boundary before slicing, with a regression test covering the case. ([#113](#113)) (@jdx) ## Security - **`rustls-webpki` bumped to 0.103.13** — Addresses [RUSTSEC-2026-0104](https://rustsec.org/advisories/RUSTSEC-2026-0104), a reachable panic in certificate revocation list parsing. Lockfile-only change. ([#107](#107)) (@jdx) ## Docs - Added a dismissible cross-site announcement banner and an en.dev footer to the documentation site, with follow-up polish (contrast, centering, z-index), smarter caching, and `ResizeObserver`-based height syncing so VitePress's nav offset stays correct on resize. ([#109](#109), [#110](#110), [#111](#111), [#112](#112)) (@jdx) --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Summary
Follow-up fix for the banner.
`--vp-layout-top-height` was set once in a single rAF and never updated. On window resize, orientation change, or text reflow the banner height could change, leaving VitePress's nav offset stale and causing overlap or a visible gap. Observe the banner element and resync the variable whenever its size changes; disconnect the observer on dismiss.
\U0001F916 Generated with Claude Code
Note
Low Risk
Low risk docs-theme tweak that only affects banner layout; potential issues are limited to browsers without
ResizeObserversupport or unexpected observer behavior.Overview
Keeps VitePress’s
--vp-layout-top-heightCSS var in sync with the announcement banner’s actual height by introducing asyncHeighthelper and observing the banner withResizeObserver.On dismiss, the observer is disconnected and the layout height var is removed, replacing the previous one-time
requestAnimationFrameheight set with ongoing updates on resize/reflow.Reviewed by Cursor Bugbot for commit 6393ae0. Bugbot is set up for automated code reviews on this repo. Configure here.