Skip to main content

logicaffeine_web/ui/
fragment.rs

1//! Pages whose route models a `#fragment` open at the element it names.
2//!
3//! The route keeps the fragment through boot normalization (`#:section` in its `#[route]`), but
4//! the browser's own jump to it cannot be relied on: the prerendered copy it jumped in is swapped
5//! for the live app, a link inside the app changes the route without loading a page, and a page
6//! whose body is fetched renders the element only once the fetch lands. So the page scrolls
7//! itself, once the element exists, honouring `prefers-reduced-motion`.
8
9/// How long to wait for the element to appear: a fetched page body (the legal pages) can take a
10/// few seconds on a slow connection.
11#[cfg(target_arch = "wasm32")]
12const WAIT_FRAMES: u32 = 600;
13
14/// Bring the element with `id` to the top of the page, under the sticky nav (the target's
15/// `scroll-margin-top` sets the gap), waiting up to ten seconds for it to be rendered.
16pub fn scroll_to_section(id: String) {
17    #[cfg(target_arch = "wasm32")]
18    wasm_bindgen_futures::spawn_local(async move {
19        for _ in 0..WAIT_FRAMES {
20            gloo_timers::future::TimeoutFuture::new(16).await;
21            let Some(window) = web_sys::window() else { return };
22            let Some(element) = window.document().and_then(|d| d.get_element_by_id(&id)) else { continue };
23            let reduced = window
24                .match_media("(prefers-reduced-motion: reduce)")
25                .ok()
26                .flatten()
27                .is_some_and(|m| m.matches());
28            let options = web_sys::ScrollIntoViewOptions::new();
29            options.set_behavior(if reduced { web_sys::ScrollBehavior::Instant } else { web_sys::ScrollBehavior::Smooth });
30            options.set_block(web_sys::ScrollLogicalPosition::Start);
31            element.scroll_into_view_with_scroll_into_view_options(&options);
32            return;
33        }
34    });
35    #[cfg(not(target_arch = "wasm32"))]
36    let _ = id;
37}