blitz-dom: `get_client_bounding_rect` moves a scroll container by its own scroll offset
Generated from repros/blitz-dom-bounding-rect-scroller-own-offset/README.md when the site was built at 7aa23e2.
What niso does
Section titled “What niso does”crates/niso-core/src/api.rs:254-260 (Dom::rect): unless the element scrolls the viewport, add the element’s
own scroll_offset() back to the rect blitz returns.
Expected vs actual
Section titled “Expected vs actual”physical_unrounded_geometry (packages/blitz-dom/src/document.rs:2433-2448) sums
location - scroll_offset over the containing-block chain starting at the node itself, so a scroller’s own
scroll offset is subtracted from its own border box. Scrolling moves the content of a scroll container, not the
container. Its descendants come out right (the content below is at -150 as expected).
overflow: auto box at y = 50, 100px tall, with 500px of content, scrolled to 200:
#scroller scrollTop = 200#scroller: expected y = 50, actual y = -150#content: expected y = -150, actual y = -150CARGO_TARGET_DIR=~/.capy/work/repro-target cargo runExit status 1 while the bug reproduces, 0 once fixed. On Ubuntu, blitz needs sudo apt-get install -y libfontconfig-dev pkg-config.
Upstream main
Section titled “Upstream main”Reproduces. Blitz main was at be60597f67b3cece0456d26e8d7ff5a88600bbc5 when tested (2026-11-08, git ls-remote https://github.com/DioxusLabs/blitz HEAD), the same commit niso pins, so the pinned result is also the main result.
Existing issue or PR
Section titled “Existing issue or PR”None found (same searches as the fixed-position repro: getBoundingClientRect, get_client_bounding_rect,
scroll offset, scroll). #874 (merged) introduced physical_unrounded_geometry but is not about this.
Issue text
Section titled “Issue text”Title: get_client_bounding_rect subtracts a scroll container’s own scroll offset from its own box
Body:
physical_unrounded_geometry starts its location - scroll_offset walk at the node itself, so a scrolled
overflow: auto element reports its border box moved by its own scrollTop/scrollLeft. Only its descendants
should move.
use blitz_dom::{DocumentConfig, ScrollBehavior};use blitz_html::HtmlDocument;use blitz_traits::shell::{ColorScheme, Viewport};
const HTML: &str = r#"<!DOCTYPE html><html><body style="margin:0"><div id="scroller" style="margin-top:50px;width:100px;height:100px;overflow:auto"><div id="content" style="height:500px"></div></div></body></html>"#;
fn main() { let config = DocumentConfig { viewport: Some(Viewport::new(800, 600, 1.0, ColorScheme::Light)), ..Default::default() }; let mut doc = HtmlDocument::from_html(HTML, config); doc.resolve(0.0); let scroller = doc.get_element_by_id("scroller").unwrap(); doc.scroll_to(scroller, 0.0, 200.0, ScrollBehavior::Instant); doc.resolve(0.0); println!("{}", doc.get_client_bounding_rect(scroller).unwrap().y); // expected 50, actual -150}Tested at be60597f67b3cece0456d26e8d7ff5a88600bbc5. A fix: add the node’s own location without subtracting its own scroll offset, and subtract
scroll_offset only for ancestors on the containing-block chain.