diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py index 3516746..e31f483 100644 --- a/src/gui/components/_legacy.py +++ b/src/gui/components/_legacy.py @@ -495,83 +495,119 @@ def render_sticky_footer() -> None: """Render a slim fixed-position footer at the bottom of the viewport. Contains a "Back to Home" link that's always visible regardless of - scroll position. Replaces the previous top + bottom-of-page - ``back_to_home_link`` buttons: a single sticky bar is more - discoverable, doesn't scroll out of view, and visually reads as a - "non-movable footer" (border-top + slim padding) rather than - just-another-button-at-the-bottom. + scroll position. The footer is mounted as a direct child of + ``
`` via a component-iframe script so it lives OUTSIDE every + Streamlit container — that matters because ``.stApp`` carries + ``zoom: 0.85`` (our compact-layout scaler) and Streamlit's content + columns add their own padding/positioning context that previously + swallowed the in-place ``st.markdown`` footer. - Implementation notes: + The implementation is two-pass: - - Uses a styled ```` anchor rather than an - ``st.button`` because Streamlit widgets can't be CSS-positioned - reliably (their DOM container is owned by Streamlit's renderer - and gets re-mounted on every rerun). A real anchor sits anywhere - we want and triggers Streamlit's URL routing to the home page. - - ``href="home"`` is a relative path so it works behind a reverse - proxy / non-root mount. - - We bump ``padding-bottom`` on the main container so the last - widget isn't hidden behind the fixed bar. - - Theme: ``rgba(255,255,255,0.96)`` background with a thin - semi-transparent border-top. The blur backdrop keeps content - slightly visible underneath. Works against both light and dark - Streamlit themes — explicit colours, not theme variables, - because Streamlit's CSS variable surface is unstable across - minor versions. + 1. ``st.markdown`` injects the CSS rules into the parent document. + Class-targeted, so the rules apply once the footer DOM node + exists regardless of where it lives. + 2. ``streamlit.components.v1.html`` renders a zero-height iframe + whose JS reaches ``window.parent.document`` and creates / moves + a ``#datatools-sticky-footer`` div directly under ````. + This bypasses every Streamlit container. + + The anchor uses ``href="home"`` (relative) so Streamlit's URL + routing resolves it to the Home page and the link works correctly + behind a reverse proxy or non-root mount. """ import html as _html - label = _html.escape(_t("nav.back_to_home")) + import json as _json + label_raw = _t("nav.back_to_home") + label_esc = _html.escape(label_raw) + + # CSS rules live in the parent document. Class selector so a + # re-rendered/relocated footer div picks them up automatically. st.markdown( - f""" + """ - """, unsafe_allow_html=True, ) + # Move the footer to directly via component iframe. The + # iframe carries allow-same-origin so window.parent.document is + # reachable; if a sandbox config ever blocks that we fall back to + # rendering inside the iframe itself (still visible, just sized + # to the iframe rather than the viewport). + from streamlit.components.v1 import html as _components_html + _components_html( + f""" + +""", + height=0, + ) + def back_to_home_link(*, key: str = "_back_to_home_link") -> None: """Render a "← Back to Home" affordance on a tool page.