From b637b78a4d083a455aa2bc087d63758364683a25 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:53:47 -0500 Subject: [PATCH] Convert sidebar modules to accordion on mobile On screens < 992px, sidebar-left and sidebar-right card modules are transformed into Bootstrap accordion items via JS. Card headers become collapsible toggles, card bodies collapse. On desktop resize the accordion reverts to normal cards. CSS scoped to mobile breakpoint styles the accordion buttons and removes card borders for a clean stacked look. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/media/css/template.css | 33 ++++++++++++++ src/media/js/template.js | 93 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/src/media/css/template.css b/src/media/css/template.css index d3f0ca8..4d2a3dc 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -15844,6 +15844,39 @@ body:not(.has-sidebar-right) .site-grid .container-component { grid-area: side-r; } +/* Sidebar accordion on mobile */ +@media (max-width: 991.98px) { + .container-sidebar-left.accordion .card, + .container-sidebar-right.accordion .card { + border: none; + border-bottom: 1px solid var(--border-color, #dee2e6); + border-radius: 0; + } + + .container-sidebar-left.accordion .card-header, + .container-sidebar-right.accordion .card-header { + padding: 0; + background: none; + border: none; + } + + .container-sidebar-left.accordion .accordion-button, + .container-sidebar-right.accordion .accordion-button { + padding: .75rem 1rem; + font-weight: 600; + font-size: .95rem; + background: var(--bs-body-bg, #fff); + color: var(--body-font-color, #444); + } + + .container-sidebar-left.accordion .accordion-button:not(.collapsed), + .container-sidebar-right.accordion .accordion-button:not(.collapsed) { + background: var(--bs-body-bg, #fff); + color: var(--link-color, #3565e5); + box-shadow: none; + } +} + .container-main-top { grid-area: main-t; } diff --git a/src/media/js/template.js b/src/media/js/template.js index 2958bae..34beca6 100644 --- a/src/media/js/template.js +++ b/src/media/js/template.js @@ -488,6 +488,98 @@ return doc.body.getAttribute('data-theme-fab-enabled') === '1'; } + /** + * Convert sidebar card modules into accordion on mobile. + * On screens <= 991px each card collapses; on desktop they revert. + */ + function initSidebarAccordion() { + var BREAKPOINT = 992; + var sidebars = doc.querySelectorAll(".container-sidebar-left, .container-sidebar-right"); + if (!sidebars.length) return; + + var accordionised = false; + + function apply() { + var isMobile = win.innerWidth < BREAKPOINT; + + if (isMobile && !accordionised) { + sidebars.forEach(function (sidebar, si) { + var accId = "sidebarAcc-" + si; + sidebar.setAttribute("id", accId); + sidebar.classList.add("accordion"); + + var cards = sidebar.querySelectorAll(":scope > .card"); + cards.forEach(function (card, ci) { + var collapseId = accId + "-c" + ci; + card.classList.add("accordion-item"); + + var header = card.querySelector(".card-header"); + var body = card.querySelector(".card-body"); + if (!header || !body) return; + + // Turn header into accordion button + header.classList.add("accordion-header"); + var btn = doc.createElement("button"); + btn.className = "accordion-button collapsed"; + btn.type = "button"; + btn.setAttribute("data-bs-toggle", "collapse"); + btn.setAttribute("data-bs-target", "#" + collapseId); + btn.setAttribute("aria-expanded", "false"); + btn.setAttribute("aria-controls", collapseId); + btn.textContent = header.textContent; + header.textContent = ""; + header.appendChild(btn); + header.setAttribute("data-moko-original-text", btn.textContent); + + // Wrap body in collapse + var wrapper = doc.createElement("div"); + wrapper.id = collapseId; + wrapper.className = "accordion-collapse collapse"; + wrapper.setAttribute("data-bs-parent", "#" + accId); + card.insertBefore(wrapper, body); + wrapper.appendChild(body); + body.classList.add("accordion-body"); + }); + }); + accordionised = true; + } else if (!isMobile && accordionised) { + // Revert to plain cards + sidebars.forEach(function (sidebar) { + sidebar.classList.remove("accordion"); + sidebar.removeAttribute("id"); + + var cards = sidebar.querySelectorAll(":scope > .card"); + cards.forEach(function (card) { + card.classList.remove("accordion-item"); + + var header = card.querySelector(".card-header"); + var btn = header ? header.querySelector(".accordion-button") : null; + if (header && btn) { + var text = header.getAttribute("data-moko-original-text") || btn.textContent; + header.removeAttribute("data-moko-original-text"); + header.classList.remove("accordion-header"); + header.textContent = text; + } + + var wrapper = card.querySelector(".accordion-collapse"); + if (wrapper) { + var body = wrapper.querySelector(".card-body"); + if (body) { + body.classList.remove("accordion-body"); + card.appendChild(body); + } + wrapper.parentNode.removeChild(wrapper); + } + }); + }); + accordionised = false; + } + } + + apply(); + win.addEventListener("resize", apply); + } + /** * Run all template JS initializations */ @@ -512,6 +604,7 @@ // Init features initDrawers(); initBackTop(); + initSidebarAccordion(); } if (doc.readyState === "loading") {