Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## v1.x.x (unreleased)

### Features

- add content width toggle to collapse sidebars and widen the content area on desktop
- add table of contents collapse toggle with initial tooltip in the top right corner
- refactor styles to align better with styleguide

## v1.2.1 (2026-05-27)

### Bug Fixes
Expand Down
156 changes: 156 additions & 0 deletions mkdocs_doubleslash_theme/assets/javascripts/content-width-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
(function () {
"use strict";

var STORAGE_KEY = "ds-content-wide";
var DESKTOP_QUERY = "(min-width: 76.25em)";
var LABEL_COLLAPSE = "Navigation ausblenden";
var LABEL_EXPAND = "Navigation einblenden";

function isWideMode() {
return document.documentElement.hasAttribute("data-ds-content-wide");
}

function setWideMode(enabled) {
if (enabled) {
document.documentElement.setAttribute("data-ds-content-wide", "");
try {
localStorage.setItem(STORAGE_KEY, "true");
} catch (e) {}
} else {
document.documentElement.removeAttribute("data-ds-content-wide");
try {
localStorage.removeItem(STORAGE_KEY);
} catch (e) {}
}
}

function sidebarIsHidden(sidebar) {
return !sidebar || sidebar.hasAttribute("hidden");
}

function hasCollapsibleSidebars() {
var primary = document.querySelector(".md-sidebar--primary");
return !sidebarIsHidden(primary);
}

function updateButtonState(button) {
var wide = isWideMode();
var collapseIcon = button.querySelector(".ds-content-width-toggle__icon--collapse");
var expandIcon = button.querySelector(".ds-content-width-toggle__icon--expand");

button.setAttribute("aria-pressed", wide ? "true" : "false");
button.setAttribute("aria-label", wide ? LABEL_EXPAND : LABEL_COLLAPSE);
button.setAttribute("title", wide ? LABEL_EXPAND : LABEL_COLLAPSE);

if (collapseIcon) {
collapseIcon.hidden = wide;
}
if (expandIcon) {
expandIcon.hidden = !wide;
}
}

function updateButtonVisibility(button, desktopMedia) {
var shouldShow = desktopMedia.matches && hasCollapsibleSidebars();
button.hidden = !shouldShow;

if (!shouldShow && isWideMode()) {
setWideMode(false);
updateButtonState(button);
document.dispatchEvent(new CustomEvent("ds-content-wide-change"));
}
}

function updateButtonPosition(button) {
if (isWideMode() || button.hidden) {
button.style.removeProperty("left");
return;
}

var content = document.querySelector(".md-content");
if (!content) {
button.style.removeProperty("left");
return;
}

// Align with .md-content border-left (the divider belongs to content, not the sidebar).
button.style.left = content.getBoundingClientRect().left + "px";
}

function scheduleButtonPositionUpdate(button) {
window.requestAnimationFrame(function () {
updateButtonPosition(button);
});
}

function init() {
var button = document.querySelector("[data-md-component='content-width-toggle']");
if (!button) {
return;
}

var desktopMedia = window.matchMedia(DESKTOP_QUERY);

try {
if (localStorage.getItem(STORAGE_KEY) === "true") {
setWideMode(true);
}
} catch (e) {}

function refreshButton(button, desktopMedia) {
updateButtonVisibility(button, desktopMedia);
updateButtonState(button);
updateButtonPosition(button);
}

refreshButton(button, desktopMedia);

button.addEventListener("click", function () {
setWideMode(!isWideMode());
refreshButton(button, desktopMedia);
document.dispatchEvent(new CustomEvent("ds-content-wide-change"));
});

desktopMedia.addEventListener("change", function () {
refreshButton(button, desktopMedia);
});

window.addEventListener("resize", function () {
refreshButton(button, desktopMedia);
});

document.addEventListener("ds-toc-collapse-change", function () {
scheduleButtonPositionUpdate(button);
});

if (typeof MutationObserver !== "undefined") {
var layoutObserver = new MutationObserver(function () {
scheduleButtonPositionUpdate(button);
});
layoutObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-ds-toc-collapsed", "data-ds-content-wide"],
});
}

if (typeof ResizeObserver !== "undefined") {
var resizeObserver = new ResizeObserver(function () {
scheduleButtonPositionUpdate(button);
});
var content = document.querySelector(".md-content");
var mainInner = document.querySelector(".md-main__inner");
if (content) {
resizeObserver.observe(content);
}
if (mainInner) {
resizeObserver.observe(mainInner);
}
}
}

if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();
162 changes: 162 additions & 0 deletions mkdocs_doubleslash_theme/assets/javascripts/toc-collapse-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
(function () {
"use strict";

var STORAGE_KEY = "ds-toc-collapsed";
var TOOLTIP_STORAGE_KEY = "ds-toc-toggle-tooltip-shown";
var DESKTOP_QUERY = "(min-width: 76.25em)";
var TOOLTIP_DURATION_MS = 3000;
var LABEL_COLLAPSE = "Inhaltsverzeichnis ausblenden";
var LABEL_EXPAND = "Inhaltsverzeichnis einblenden";

function isTocCollapsed() {
return document.documentElement.hasAttribute("data-ds-toc-collapsed");
}

function setTocCollapsed(enabled) {
if (enabled) {
document.documentElement.setAttribute("data-ds-toc-collapsed", "");
try {
localStorage.setItem(STORAGE_KEY, "true");
} catch (e) {}
} else {
document.documentElement.removeAttribute("data-ds-toc-collapsed");
try {
localStorage.removeItem(STORAGE_KEY);
} catch (e) {}
}
}

function tocSidebarIsHidden() {
var secondary = document.querySelector(".md-sidebar--secondary");
return !secondary || secondary.hasAttribute("hidden");
}

function updateButtonState(button) {
var collapsed = isTocCollapsed();
var collapseIcon = button.querySelector(".ds-toc-toggle__icon--collapse");
var expandIcon = button.querySelector(".ds-toc-toggle__icon--expand");

button.setAttribute("aria-pressed", collapsed ? "true" : "false");
button.setAttribute("aria-label", collapsed ? LABEL_EXPAND : LABEL_COLLAPSE);
button.setAttribute("title", collapsed ? LABEL_EXPAND : LABEL_COLLAPSE);

if (collapseIcon) {
collapseIcon.hidden = collapsed;
}
if (expandIcon) {
expandIcon.hidden = !collapsed;
}
}

function hideTooltip(tooltip) {
if (!tooltip || tooltip.hidden) {
return;
}
tooltip.classList.remove("ds-toc-toggle__tooltip--visible");
window.setTimeout(function () {
tooltip.hidden = true;
}, 200);
}

function updateButtonVisibility(button, tooltip, desktopMedia) {
var wasVisible = !button.hidden;
var shouldShow = desktopMedia.matches && !tocSidebarIsHidden();
button.hidden = !shouldShow;

if (!shouldShow) {
if (wasVisible) {
hideTooltip(tooltip);
}
if (isTocCollapsed()) {
setTocCollapsed(false);
updateButtonState(button);
}
return;
}

maybeShowInitialTooltip(button, tooltip);
}

function maybeShowInitialTooltip(button, tooltip) {
if (!tooltip || button.hidden) {
return;
}

try {
if (sessionStorage.getItem(TOOLTIP_STORAGE_KEY) === "true") {
return;
}
} catch (e) {
return;
}

showInitialTooltip(tooltip, button);
}

function showInitialTooltip(tooltip, button) {
if (!tooltip || tooltip.classList.contains("ds-toc-toggle__tooltip--visible")) {
return;
}

try {
sessionStorage.setItem(TOOLTIP_STORAGE_KEY, "true");
} catch (e) {}

tooltip.hidden = false;
window.requestAnimationFrame(function () {
tooltip.classList.add("ds-toc-toggle__tooltip--visible");
});

window.setTimeout(function () {
hideTooltip(tooltip);
}, TOOLTIP_DURATION_MS);

button.addEventListener(
"click",
function () {
hideTooltip(tooltip);
},
{ once: true }
);
}

function init() {
var button = document.querySelector("[data-md-component='toc-collapse-toggle']");
var tooltip = document.querySelector("[data-md-component='toc-collapse-tooltip']");
if (!button) {
return;
}

var desktopMedia = window.matchMedia(DESKTOP_QUERY);

try {
if (localStorage.getItem(STORAGE_KEY) === "true") {
setTocCollapsed(true);
}
} catch (e) {}

updateButtonState(button);
updateButtonVisibility(button, tooltip, desktopMedia);

button.addEventListener("click", function () {
setTocCollapsed(!isTocCollapsed());
updateButtonState(button);
document.dispatchEvent(new CustomEvent("ds-toc-collapse-change"));
});

desktopMedia.addEventListener("change", function () {
updateButtonVisibility(button, tooltip, desktopMedia);
updateButtonState(button);
});

window.addEventListener("resize", function () {
updateButtonVisibility(button, tooltip, desktopMedia);
});
}

if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();
Loading
Loading