From dc3c26edd9a5783feac5375e3439eff1b9e516c5 Mon Sep 17 00:00:00 2001 From: Boris Batkin Date: Fri, 24 Jul 2026 12:34:07 -0700 Subject: [PATCH] docs: add hideable navigation sidebar --- doc/source/_static/custom.css | 83 +++++++++++++++++++++++++ doc/source/_static/sidebar.js | 100 ++++++++++++++++++++++++++++++ doc/source/_templates/layout.html | 14 +++++ doc/source/conf.py | 2 + 4 files changed, 199 insertions(+) create mode 100644 doc/source/_static/sidebar.js diff --git a/doc/source/_static/custom.css b/doc/source/_static/custom.css index 28db943..c55c757 100644 --- a/doc/source/_static/custom.css +++ b/doc/source/_static/custom.css @@ -42,6 +42,89 @@ body, .wy-body-for-nav { background: var(--bg-2) !important; border-right: 1px solid var(--rule); } + +/* Desktop/tablet sidebar toggle. sphinx_rtd_theme owns the <=768px off-canvas + * menu, so these rules deliberately start at 769px. */ +.forge-sidebar-toggle { + display: none; +} + +@media screen and (min-width: 769px) { + .wy-nav-side, + .wy-nav-content-wrap, + .forge-sidebar-toggle { + transition: + transform 160ms ease, + margin-left 160ms ease, + left 160ms ease; + } + + .forge-sidebar-toggle { + align-items: center; + background: var(--bg-2); + border: 1px solid var(--rule); + border-left: 0; + border-radius: 0 4px 4px 0; + color: var(--fg-dim); + display: flex; + height: 48px; + justify-content: center; + left: 300px; + padding: 0; + position: fixed; + top: 50%; + transform: translateY(-50%); + width: 28px; + z-index: 201; + } + + .forge-sidebar-toggle:hover { + background: var(--bg-3); + color: var(--amber); + } + + .forge-sidebar-toggle:focus-visible { + color: var(--amber); + outline: 2px solid var(--amber); + outline-offset: 2px; + } + + .forge-sidebar-toggle-label { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; + } + + html.forge-sidebar-collapsed .wy-nav-side { + transform: translateX(-100%); + } + + html.forge-sidebar-collapsed .wy-nav-content-wrap { + margin-left: 0; + } + + html.forge-sidebar-collapsed .wy-nav-content { + margin-left: auto; + margin-right: auto; + } + + html.forge-sidebar-collapsed .forge-sidebar-toggle { + left: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + .wy-nav-side, + .wy-nav-content-wrap, + .forge-sidebar-toggle { + transition: none; + } +} + .wy-side-nav-search { background: var(--bg) !important; border-bottom: 1px solid var(--rule); diff --git a/doc/source/_static/sidebar.js b/doc/source/_static/sidebar.js new file mode 100644 index 0000000..f4501f4 --- /dev/null +++ b/doc/source/_static/sidebar.js @@ -0,0 +1,100 @@ +// Hideable desktop/tablet navigation for sphinx_rtd_theme. +// +// The theme already provides an off-canvas hamburger at <=768px. Above that, +// default to a reading-first layout on narrower screens while remembering an +// explicit user choice across documentation pages. +(function () { + 'use strict'; + + var STORAGE_KEY = 'forge-docs-sidebar'; + var COLLAPSED_CLASS = 'forge-sidebar-collapsed'; + var desktopMedia = window.matchMedia('(min-width: 769px)'); + var narrowDesktopMedia = window.matchMedia('(max-width: 1099px)'); + var root = document.documentElement; + + function readPreference() { + try { + var value = window.localStorage.getItem(STORAGE_KEY); + return value === 'collapsed' || value === 'expanded' ? value : null; + } catch (_) { + return null; + } + } + + function writePreference(value) { + try { + window.localStorage.setItem(STORAGE_KEY, value); + } catch (_) { + // Storage can be disabled; the toggle should still work for this page. + } + } + + function shouldCollapse() { + if (!desktopMedia.matches) return false; + var preference = readPreference(); + if (preference) return preference === 'collapsed'; + return narrowDesktopMedia.matches; + } + + // Apply the initial class before deferred DOM setup to avoid a visible + // sidebar jump on reading-first tablet/narrow-desktop layouts. + root.classList.toggle(COLLAPSED_CLASS, shouldCollapse()); + + function init() { + var navigation = document.querySelector('.wy-nav-side'); + var toggle = document.querySelector('.forge-sidebar-toggle'); + if (!navigation || !toggle) return; + + navigation.id = 'forge-site-navigation'; + + var icon = toggle.querySelector('.fa'); + var label = toggle.querySelector('.forge-sidebar-toggle-label'); + + function updateToggle() { + var collapsed = root.classList.contains(COLLAPSED_CLASS); + toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + toggle.setAttribute( + 'title', + collapsed ? 'Show navigation' : 'Hide navigation' + ); + if (label) { + label.textContent = collapsed ? 'Show navigation' : 'Hide navigation'; + } + if (icon) { + icon.classList.toggle('fa-chevron-left', !collapsed); + icon.classList.toggle('fa-chevron-right', collapsed); + } + } + + function syncLayout() { + root.classList.toggle(COLLAPSED_CLASS, shouldCollapse()); + if (!desktopMedia.matches) { + // Always enter mobile with the theme's off-canvas menu closed. + document + .querySelectorAll("[data-toggle='wy-nav-shift']") + .forEach(function (element) { + element.classList.remove('shift'); + }); + } + updateToggle(); + } + + toggle.addEventListener('click', function () { + if (!desktopMedia.matches) return; + var collapsed = !root.classList.contains(COLLAPSED_CLASS); + root.classList.toggle(COLLAPSED_CLASS, collapsed); + writePreference(collapsed ? 'collapsed' : 'expanded'); + updateToggle(); + }); + + desktopMedia.addEventListener('change', syncLayout); + narrowDesktopMedia.addEventListener('change', syncLayout); + syncLayout(); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/doc/source/_templates/layout.html b/doc/source/_templates/layout.html index 8e4c7b9..96fbae4 100644 --- a/doc/source/_templates/layout.html +++ b/doc/source/_templates/layout.html @@ -1,5 +1,19 @@ {%- extends "!layout.html" %} +{# Desktop/tablet navigation toggle. The RTD theme supplies its own off-canvas + hamburger below 769px; this control is hidden there. sidebar.js adds the + controlled navigation id and keeps the accessible label/state in sync. #} +{%- block extrabody %} + {{ super() }} + +{%- endblock %} + {# Override sphinx_rtd_theme's sidebar brand so the logo links to the daslang.io home page instead of pathto(_root_doc). Mirrors the upstream block at sphinx_rtd_theme/layout.html sidebartitle, with the diff --git a/doc/source/conf.py b/doc/source/conf.py index 6a3d3ad..68174a5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -73,4 +73,6 @@ # the daslang docs theme evolves. html_css_files = ['custom.css', 'custom-patch.css'] +html_js_files = ['sidebar.js'] + htmlhelp_basename = 'dasimguiimplot_doc'