Skip to content

docs: keep banner height in sync via ResizeObserver#9330

Merged
jdx merged 1 commit intomainfrom
feat/banner-resize-observer
Apr 23, 2026
Merged

docs: keep banner height in sync via ResizeObserver#9330
jdx merged 1 commit intomainfrom
feat/banner-resize-observer

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 23, 2026

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-only UI behavior change that updates a CSS variable on resize/reflow; main risk is minor layout/perf issues if observation behaves unexpectedly in some browsers.

Overview
Keeps the VitePress top layout offset (--vp-layout-top-height) in sync with the docs announcement banner by extracting a syncHeight() helper and wiring it to a ResizeObserver.

On dismiss, the observer is disconnected before removing the banner and clearing the CSS variable; initial height is still set via requestAnimationFrame, with a graceful no-ResizeObserver fallback.

Reviewed by Cursor Bugbot for commit 974aafb. Bugbot is set up for automated code reviews on this repo. Configure here.

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]>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR replaces the one-shot requestAnimationFrame height sync with a ResizeObserver-based approach, ensuring --vp-layout-top-height stays accurate across window resizes, orientation changes, and text reflow. The change is minimal and well-structured, with proper cleanup on dismiss and an SSR/old-browser guard.

Confidence Score: 5/5

Safe to merge — small, focused fix with no logic issues.

The change is minimal and correct. The ResizeObserver guard handles SSR and older browsers, cleanup on dismiss is properly handled, and the retained requestAnimationFrame gives an early initial sync for browsers where ResizeObserver may fire slightly later. No P0/P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
docs/.vitepress/theme/banner.ts Adds ResizeObserver to keep --vp-layout-top-height in sync with the banner element's height; observer is guarded for SSR/old-browser compatibility and correctly disconnected on dismiss.

Sequence Diagram

sequenceDiagram
    participant Fetch as fetch(banner.json)
    participant Render as render()
    participant DOM as document.body
    participant RAF as requestAnimationFrame
    participant RO as ResizeObserver
    participant CSS as CSS variable

    Fetch->>Render: banner data
    Render->>DOM: prepend(el)
    Render->>RAF: schedule syncHeight
    Render->>RO: observe(el)
    RAF->>CSS: set --vp-layout-top-height (initial)
    RO->>CSS: set --vp-layout-top-height (on every resize)
    note over RO,CSS: Fires on resize, orientation change, text reflow
    DOM-->>RO: dismiss click
    RO->>RO: disconnect()
    DOM->>CSS: removeProperty(--vp-layout-top-height)
Loading

Reviews (1): Last reviewed commit: "docs: keep --vp-layout-top-height in syn..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a ResizeObserver to dynamically synchronize the banner's height with the --vp-layout-top-height CSS variable. A review comment identified a potential race condition where the height synchronization logic could execute after the banner is dismissed, potentially resetting the CSS variable to zero; a suggestion was made to add a connection check to prevent this behavior.

Comment on lines +54 to +59
const syncHeight = () => {
document.documentElement.style.setProperty(
"--vp-layout-top-height",
`${el.offsetHeight}px`,
);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

There is a potential race condition here. If the banner is dismissed (via the button click) before the requestAnimationFrame or a ResizeObserver notification fires, syncHeight will execute on a detached element. This will result in el.offsetHeight being 0, which then re-adds the --vp-layout-top-height property to the document as 0px after it was explicitly removed in the click handler. Adding a check for el.isConnected ensures the property is only updated if the banner is still active in the DOM.

Suggested change
const syncHeight = () => {
document.documentElement.style.setProperty(
"--vp-layout-top-height",
`${el.offsetHeight}px`,
);
};
const syncHeight = () => {
if (!el.isConnected) return;
document.documentElement.style.setProperty(
"--vp-layout-top-height",
`${el.offsetHeight}px`,
);
};

@jdx jdx merged commit fc1e58f into main Apr 23, 2026
37 checks passed
@jdx jdx deleted the feat/banner-resize-observer branch April 23, 2026 19:49
@github-actions
Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.19 x -- echo 25.0 ± 0.5 23.9 26.6 1.00
mise x -- echo 25.8 ± 1.0 24.7 39.2 1.03 ± 0.05

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.19 env 24.6 ± 0.6 23.4 30.4 1.00
mise env 25.5 ± 0.6 24.4 27.9 1.03 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.19 hook-env 25.6 ± 0.5 24.2 27.4 1.00
mise hook-env 26.4 ± 0.4 25.2 28.2 1.03 ± 0.03

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.19 ls 23.1 ± 1.1 21.9 39.3 1.00
mise ls 23.3 ± 0.4 22.3 26.2 1.01 ± 0.05

xtasks/test/perf

Command mise-2026.4.19 mise Variance
install (cached) 170ms 175ms -2%
ls (cached) 81ms 82ms -1%
bin-paths (cached) 86ms 88ms -2%
task-ls (cached) 811ms 802ms +1%

mise-en-dev added a commit that referenced this pull request Apr 24, 2026
### 🐛 Bug Fixes

- **(config)** resolve relative path: tool versions against config root
by @jdx in [#9320](#9320)
- **(lock)** resolve @latest and prune poisoned lockfile entries by @jdx
in [#9321](#9321)
- fix - be able to work with regex in attestation check by @monotek in
[#9327](#9327)

### 🚜 Refactor

- **(aqua)** bake aqua registry from merged yaml by @risu729 in
[#9043](#9043)

### 📚 Documentation

- add cross-site announcement banner by @jdx in
[#9326](#9326)
- keep banner height in sync via ResizeObserver by @jdx in
[#9330](#9330)
- respect banner expires field by @jdx in
[#9334](#9334)

### 📦️ Dependency Updates

- bump communique to 1.0.2 by @jdx in
[#9313](#9313)
- bump communique to 1.0.3 by @jdx in
[#9332](#9332)
- update actions/setup-node digest to 48b55a0 by @renovate[bot] in
[#9339](#9339)
- update ghcr.io/jdx/mise:alpine docker digest to a92efa5 by
@renovate[bot] in [#9340](#9340)
- update ghcr.io/jdx/mise:rpm docker digest to 5c24f69 by @renovate[bot]
in [#9343](#9343)
- update rust docker digest to e4f09e8 by @renovate[bot] in
[#9345](#9345)
- update rui314/setup-mold digest to 9c9c13b by @renovate[bot] in
[#9344](#9344)
- update ghcr.io/jdx/mise:deb docker digest to a3afe3e by @renovate[bot]
in [#9342](#9342)
- update ghcr.io/jdx/mise:copr docker digest to 4098d5a by
@renovate[bot] in [#9341](#9341)
- update taiki-e/install-action digest to 74e87cb by @renovate[bot] in
[#9346](#9346)

### Chore

- **(ci)** remove cargo-vendor install from ppa publish by @jdx in
[#9312](#9312)
- **(release)** publish snap to stable channel by @jdx in
[#9318](#9318)
- remove FUNDING.yml in favor of jdx/.github default by @jdx in
[#9331](#9331)

## 📦 Aqua Registry

Updated [aqua-registry](https://github.com/aquaproj/aqua-registry):
[v4.492.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.492.0)
->
[v4.498.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.498.0).

Included aqua-registry releases:

-
[v4.493.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.493.0)
-
[v4.494.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.494.0)
-
[v4.494.1](https://github.com/aquaproj/aqua-registry/releases/tag/v4.494.1)
-
[v4.495.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.495.0)
-
[v4.496.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.496.0)
-
[v4.497.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.497.0)
-
[v4.498.0](https://github.com/aquaproj/aqua-registry/releases/tag/v4.498.0)
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.

1 participant