Docs: Improved the navigation seperating out ToC from top nac bar
This commit is contained in:
@@ -10,6 +10,9 @@
|
||||
* - When the reader scrolls past a section, any items that were only
|
||||
* auto-expanded (not user-pinned) collapse back down.
|
||||
* - Works with mkdocs-material instant-loading / SPA navigation.
|
||||
*
|
||||
* Page navigation is handled by the top navbar tabs + dropdowns.
|
||||
* This script only manages the TOC (md-nav--secondary) in the left sidebar.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
@@ -68,11 +71,9 @@
|
||||
//
|
||||
// Scans `containerEl` for <li> items with nested <nav> children and
|
||||
// turns them into collapsible items: adds the --nested class, collapses
|
||||
// them, and injects a toggle chevron. This is used both for the active
|
||||
// page's built-in TOC (md-nav--secondary) and for TOC items lazily
|
||||
// injected into converted single-page nav sections.
|
||||
// them, and injects a toggle chevron.
|
||||
|
||||
function makeItemsCollapsible(containerEl, trackPinning) {
|
||||
function makeItemsCollapsible(containerEl) {
|
||||
containerEl.querySelectorAll("li.md-nav__item").forEach(function (li) {
|
||||
var nestedNav = li.querySelector(":scope > nav.md-nav, :scope > .md-nav");
|
||||
if (!nestedNav) return;
|
||||
@@ -101,10 +102,10 @@
|
||||
var isCollapsed = li.classList.contains("toc-collapsed");
|
||||
if (isCollapsed) {
|
||||
expand(li);
|
||||
if (trackPinning) userPinned.add(li);
|
||||
userPinned.add(li);
|
||||
} else {
|
||||
collapse(li);
|
||||
if (trackPinning) userPinned.delete(li);
|
||||
userPinned.delete(li);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -121,25 +122,27 @@
|
||||
userPinned.clear();
|
||||
autoExpanded.clear();
|
||||
|
||||
// Force the TOC visible. Material's base CSS hides the secondary
|
||||
// nav via a sibling-combinator rule keyed off the #__toc checkbox.
|
||||
// We force-check it and set inline display so the TOC is guaranteed
|
||||
// visible regardless of the CSS cascade.
|
||||
var tocCheckbox = sidebar.querySelector("#__toc");
|
||||
if (tocCheckbox) {
|
||||
tocCheckbox.checked = true;
|
||||
}
|
||||
|
||||
// Find the actual TOC (md-nav--secondary). We scope to the secondary
|
||||
// nav to avoid touching the page-level nav items that wrap sections —
|
||||
// those use Material's native checkbox-based toggle and should not be
|
||||
// overridden.
|
||||
//
|
||||
// NOTE: The primary nav already has .md-nav--integrated added by
|
||||
// mkdocs-material when the toc.integrate feature is enabled. We do
|
||||
// NOT re-add that class or extend it to descendant navs — doing so
|
||||
// causes the CSS rule ".md-nav--integrated .md-nav__item--nested >
|
||||
// .md-nav { display: block }" to override Material's native
|
||||
// checkbox-based collapse on primary nav sections (Reference,
|
||||
// Development, Architecture Decisions, etc.), creating whitespace
|
||||
// below collapsed section headings.
|
||||
// nav to avoid touching the page-level nav items that wrap sections.
|
||||
var tocNav = sidebar.querySelector("nav.md-nav--secondary");
|
||||
if (!tocNav) return;
|
||||
|
||||
tocNav.style.display = "block";
|
||||
tocNav.style.opacity = "1";
|
||||
tocNav.style.visibility = "visible";
|
||||
|
||||
// Make all nested TOC items collapsible, with pinning support for
|
||||
// the active-link watcher.
|
||||
makeItemsCollapsible(tocNav, true);
|
||||
makeItemsCollapsible(tocNav);
|
||||
|
||||
// Kick off the active-link watcher
|
||||
watchActiveLink();
|
||||
@@ -229,336 +232,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ── Convert leaf-page nav items into section-like structures ─────────
|
||||
//
|
||||
// Material renders leaf-page nav items (any <li> with just an <a> link
|
||||
// and no nested <nav>) as flat links with no expand/collapse chevron.
|
||||
// Only native sections (Reference, Development, Architecture Decisions)
|
||||
// get a checkbox + label + nested <nav>.
|
||||
//
|
||||
// This function converts leaf-page items at two levels:
|
||||
// 1. Top-level items (Specification, Work Remaining, FAQ)
|
||||
// 2. Children inside native sections (Action CLI, Session Model, etc.)
|
||||
//
|
||||
// It explicitly avoids touching items inside md-nav--secondary (the TOC)
|
||||
// or md-nav--page-toc (our own lazy-loaded TOC containers).
|
||||
//
|
||||
// The ACTIVE page (wherever it sits in the tree) is treated specially:
|
||||
// it becomes an always-expanded, non-collapsible section with a
|
||||
// <label> heading instead of an <a>, matching how Material handles
|
||||
// native sections that contain the current page.
|
||||
|
||||
/** Counter for unique checkbox IDs across conversions. */
|
||||
var pageNavCounter = 100;
|
||||
|
||||
/** Cache of fetched TOC HTML keyed by page URL. */
|
||||
var tocCache = {};
|
||||
|
||||
/**
|
||||
* Collect the <ul> lists whose direct <li> children should be
|
||||
* converted. This includes:
|
||||
* - The top-level nav list (direct child of md-nav--primary)
|
||||
* - The child lists inside native sections (level-1 <nav> elements
|
||||
* that are NOT md-nav--secondary and NOT md-nav--page-toc)
|
||||
*/
|
||||
function collectConvertibleLists(primaryNav) {
|
||||
var lists = [];
|
||||
|
||||
// 1. Top-level list
|
||||
var topList = primaryNav.querySelector(":scope > .md-nav__list");
|
||||
if (topList) lists.push(topList);
|
||||
|
||||
// 2. Lists inside native sections (one level deep)
|
||||
if (topList) {
|
||||
topList.querySelectorAll(":scope > li.md-nav__item--nested").forEach(function (sectionLi) {
|
||||
var sectionNav = sectionLi.querySelector(":scope > nav.md-nav:not(.md-nav--secondary):not(.md-nav--page-toc)");
|
||||
if (sectionNav) {
|
||||
var sectionList = sectionNav.querySelector(":scope > .md-nav__list");
|
||||
if (sectionList) lists.push(sectionList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert leaf-page <li> items into section-like expandable structures.
|
||||
* Only operates on items in the top-level nav and inside native sections.
|
||||
*/
|
||||
function convertPageItems() {
|
||||
var primaryNav = document.querySelector("nav.md-nav--primary");
|
||||
if (!primaryNav) return;
|
||||
|
||||
var lists = collectConvertibleLists(primaryNav);
|
||||
|
||||
lists.forEach(function (ul) {
|
||||
// Only process DIRECT children of each list
|
||||
var children = ul.querySelectorAll(":scope > li.md-nav__item");
|
||||
children.forEach(function (li) {
|
||||
// Skip items already converted or that are native sections
|
||||
if (li.classList.contains("md-nav__item--nested")) return;
|
||||
|
||||
// Active page items need special handling — always-expanded,
|
||||
// non-collapsible, matching native section behaviour.
|
||||
if (li.classList.contains("md-nav__item--active")) {
|
||||
convertActivePageItem(li);
|
||||
return;
|
||||
}
|
||||
|
||||
convertInactivePageItem(li);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an INACTIVE leaf-page <li> into a section-like structure
|
||||
* with a checkbox + label + nested nav that lazy-loads the page's TOC.
|
||||
*/
|
||||
function convertInactivePageItem(li) {
|
||||
var link = li.querySelector(":scope > a.md-nav__link");
|
||||
if (!link) return;
|
||||
|
||||
var href = link.getAttribute("href");
|
||||
var text = link.textContent.trim();
|
||||
var navId = "__nav_" + (pageNavCounter++);
|
||||
|
||||
// Build the checkbox
|
||||
var checkbox = document.createElement("input");
|
||||
checkbox.className = "md-nav__toggle md-toggle";
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.id = navId;
|
||||
|
||||
// Build the label (with chevron icon) — identical to native sections
|
||||
var label = document.createElement("label");
|
||||
label.className = "md-nav__link";
|
||||
label.setAttribute("for", navId);
|
||||
label.tabIndex = 0;
|
||||
|
||||
var ellipsis = document.createElement("span");
|
||||
ellipsis.className = "md-ellipsis";
|
||||
ellipsis.textContent = text;
|
||||
label.appendChild(ellipsis);
|
||||
|
||||
var icon = document.createElement("span");
|
||||
icon.className = "md-nav__icon md-icon";
|
||||
label.appendChild(icon);
|
||||
|
||||
// Build the nested nav with the original link as first child
|
||||
var nav = document.createElement("nav");
|
||||
nav.className = "md-nav md-nav--page-toc";
|
||||
nav.setAttribute("data-md-level", "1");
|
||||
nav.setAttribute("aria-expanded", "false");
|
||||
|
||||
var ul = document.createElement("ul");
|
||||
ul.className = "md-nav__list";
|
||||
|
||||
// First child: a link to the page itself (like section index pages)
|
||||
var indexLi = document.createElement("li");
|
||||
indexLi.className = "md-nav__item";
|
||||
var indexLink = document.createElement("a");
|
||||
indexLink.className = "md-nav__link";
|
||||
indexLink.href = href;
|
||||
var indexSpan = document.createElement("span");
|
||||
indexSpan.className = "md-ellipsis";
|
||||
indexSpan.textContent = text;
|
||||
indexLink.appendChild(indexSpan);
|
||||
indexLi.appendChild(indexLink);
|
||||
ul.appendChild(indexLi);
|
||||
|
||||
nav.appendChild(ul);
|
||||
|
||||
// Replace the <li> contents
|
||||
li.innerHTML = "";
|
||||
li.classList.add("md-nav__item--nested");
|
||||
li.appendChild(checkbox);
|
||||
li.appendChild(label);
|
||||
li.appendChild(nav);
|
||||
|
||||
// Lazy-load the TOC when first expanded
|
||||
checkbox.addEventListener("change", function () {
|
||||
if (!checkbox.checked) return;
|
||||
if (tocCache[href]) {
|
||||
injectToc(ul, tocCache[href]);
|
||||
return;
|
||||
}
|
||||
loadToc(href, ul);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ACTIVE page item to look like a native expanded section.
|
||||
*
|
||||
* Material emits a __toc checkbox + label (with a TOC-list icon) plus
|
||||
* an <a> link and the md-nav--secondary TOC nav. We hide the __toc
|
||||
* machinery and replace the <a> with a non-navigating <label> that
|
||||
* has the same section-heading + chevron appearance as native sections.
|
||||
*
|
||||
* The TOC stays always-visible and cannot be collapsed — matching how
|
||||
* Material treats native sections that contain the current page.
|
||||
*/
|
||||
function convertActivePageItem(li) {
|
||||
// 1. Force-check and hide the __toc checkbox so Material keeps the
|
||||
// TOC visible, and prevent any future toggling.
|
||||
var tocCheckbox = li.querySelector(":scope > #__toc");
|
||||
if (tocCheckbox) {
|
||||
tocCheckbox.checked = true;
|
||||
tocCheckbox.disabled = true;
|
||||
tocCheckbox.style.display = "none";
|
||||
}
|
||||
|
||||
// 2. Hide the __toc label (it has the wrong icon and toggles the
|
||||
// checkbox — we don't want either).
|
||||
var tocLabel = li.querySelector(':scope > label[for="__toc"]');
|
||||
if (tocLabel) {
|
||||
tocLabel.style.display = "none";
|
||||
}
|
||||
|
||||
// 3. Mark the <li> as nested + our custom marker so CSS can style it.
|
||||
li.classList.add("md-nav__item--nested");
|
||||
li.classList.add("md-nav__item--section-page");
|
||||
|
||||
// 4. Replace the <a> with a non-navigating <label> that matches the
|
||||
// exact structure of native section headings.
|
||||
var link = li.querySelector(":scope > a.md-nav__link");
|
||||
if (link) {
|
||||
var text = link.textContent.trim();
|
||||
|
||||
var heading = document.createElement("label");
|
||||
heading.className = "md-nav__link";
|
||||
heading.tabIndex = 0;
|
||||
|
||||
var ellipsis = document.createElement("span");
|
||||
ellipsis.className = "md-ellipsis";
|
||||
ellipsis.textContent = text;
|
||||
heading.appendChild(ellipsis);
|
||||
|
||||
var icon = document.createElement("span");
|
||||
icon.className = "md-nav__icon md-icon";
|
||||
heading.appendChild(icon);
|
||||
|
||||
link.parentNode.replaceChild(heading, link);
|
||||
}
|
||||
|
||||
// 5. Force the TOC nav always visible (belt-and-suspenders alongside
|
||||
// the CSS rule).
|
||||
var tocNav = li.querySelector(":scope > nav.md-nav--secondary");
|
||||
if (tocNav) {
|
||||
tocNav.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a page and extract its TOC <ul> content, then inject it.
|
||||
*/
|
||||
function loadToc(href, targetUl) {
|
||||
// Add a placeholder
|
||||
var placeholder = document.createElement("li");
|
||||
placeholder.className = "md-nav__item md-nav__toc-placeholder";
|
||||
placeholder.textContent = "Loading…";
|
||||
targetUl.appendChild(placeholder);
|
||||
|
||||
fetch(href)
|
||||
.then(function (res) { return res.text(); })
|
||||
.then(function (html) {
|
||||
// Remove placeholder
|
||||
if (placeholder.parentNode) placeholder.parentNode.removeChild(placeholder);
|
||||
|
||||
// Parse the fetched page and extract TOC items
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, "text/html");
|
||||
var tocList = doc.querySelector("[data-md-component='toc']");
|
||||
if (!tocList) return;
|
||||
|
||||
// Cache the TOC items (as an array of <li> outerHTML)
|
||||
var items = [];
|
||||
tocList.querySelectorAll(":scope > li").forEach(function (li) {
|
||||
items.push(li.outerHTML);
|
||||
});
|
||||
tocCache[href] = items;
|
||||
injectToc(targetUl, items);
|
||||
})
|
||||
.catch(function () {
|
||||
if (placeholder.parentNode) placeholder.parentNode.removeChild(placeholder);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject cached TOC items into the target <ul>.
|
||||
*/
|
||||
function injectToc(targetUl, items) {
|
||||
// Don't inject if already done
|
||||
if (targetUl.dataset.tocLoaded) return;
|
||||
targetUl.dataset.tocLoaded = "true";
|
||||
|
||||
items.forEach(function (itemHtml) {
|
||||
var temp = document.createElement("template");
|
||||
temp.innerHTML = itemHtml;
|
||||
var li = temp.content.firstElementChild;
|
||||
if (li) {
|
||||
// Fix relative anchor links — they need the page href prefix
|
||||
li.querySelectorAll("a[href^='#']").forEach(function (a) {
|
||||
var pageLink = targetUl.querySelector("li:first-child a");
|
||||
if (pageLink) {
|
||||
a.href = pageLink.href + a.getAttribute("href");
|
||||
}
|
||||
});
|
||||
targetUl.appendChild(li);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialise collapse/expand behaviour on any nested items that
|
||||
// were just injected (sub-sections inside the fetched TOC).
|
||||
// No pinning tracking — these are non-active pages with no
|
||||
// MutationObserver watching them.
|
||||
makeItemsCollapsible(targetUl, false);
|
||||
}
|
||||
|
||||
// ── Lock active ancestor sections ────────────────────────────────────
|
||||
//
|
||||
// When you are viewing a page inside a section (e.g. CI/CD Pipeline
|
||||
// inside Development), Material marks every ancestor section <li> with
|
||||
// md-nav__item--active and checks its toggle. By default the user can
|
||||
// still uncheck (collapse) those sections. We prevent this so that
|
||||
// every section on the path to the active page stays permanently open,
|
||||
// matching the intuitive expectation that you cannot collapse a section
|
||||
// you are currently inside.
|
||||
|
||||
function lockActiveAncestorSections() {
|
||||
var primaryNav = document.querySelector("nav.md-nav--primary");
|
||||
if (!primaryNav) return;
|
||||
|
||||
// Find native sections that are ancestors of the current page.
|
||||
// These have both --active (propagated up from the active page)
|
||||
// and --nested (they are real sections, not converted page items).
|
||||
primaryNav.querySelectorAll(
|
||||
"li.md-nav__item--active.md-nav__item--nested:not(.md-nav__item--section-page)"
|
||||
).forEach(function (li) {
|
||||
var toggle = li.querySelector(":scope > input.md-nav__toggle");
|
||||
if (!toggle) return;
|
||||
|
||||
// Force checked and prevent user from unchecking
|
||||
toggle.checked = true;
|
||||
|
||||
// Intercept click/change to keep it locked
|
||||
toggle.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
toggle.addEventListener("change", function () {
|
||||
toggle.checked = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Desktop detection ────────────────────────────────────────────────
|
||||
//
|
||||
// This entire script is designed for the desktop persistent sidebar
|
||||
// (≥76.25em). On mobile, Material uses a slide-out drawer with its
|
||||
// own drill-down navigation, scroll management, and back-button
|
||||
// behaviour. Running our DOM restructuring (convertPageItems,
|
||||
// initCollapsibleToc, lockActiveAncestorSections) on mobile breaks
|
||||
// the drawer's scroll container and makes it impossible to scroll
|
||||
// past the visible items.
|
||||
// behaviour. Running our DOM restructuring on mobile breaks the
|
||||
// drawer's scroll container and makes it impossible to scroll past
|
||||
// the visible items.
|
||||
//
|
||||
// We use matchMedia to detect the breakpoint and only bootstrap on
|
||||
// desktop. On resize/orientation changes that cross the breakpoint,
|
||||
@@ -576,8 +257,6 @@
|
||||
function bootstrap() {
|
||||
if (!isDesktop()) return; // skip all DOM manipulation on mobile
|
||||
initCollapsibleToc();
|
||||
convertPageItems();
|
||||
lockActiveAncestorSections();
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
@@ -589,9 +268,6 @@
|
||||
// Re-init after instant navigation (mkdocs-material SPA mode)
|
||||
if (typeof document$ !== "undefined") {
|
||||
document$.subscribe(function () {
|
||||
// Reset TOC cache and counter on navigation
|
||||
tocCache = {};
|
||||
pageNavCounter = 100;
|
||||
bootstrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
{#-
|
||||
Custom tabs partial — adds dropdown menus for sections with children.
|
||||
|
||||
For top-level nav items that have children (Development, Reference, ADRs),
|
||||
a hover-activated dropdown lists all child pages. Single-page tabs
|
||||
(Specification, Timeline, FAQ) render as plain links.
|
||||
|
||||
Replaces the default Material tabs.html + tabs-item.html.
|
||||
-#}
|
||||
|
||||
{#- Recursive macro: drill to the first leaf page in a section tree. -#}
|
||||
{% macro first_leaf_url(nav_item) %}
|
||||
{% if nav_item.children %}
|
||||
{% set first = nav_item.children | first %}
|
||||
{{ first_leaf_url(first) }}
|
||||
{% else %}
|
||||
{{ nav_item.url | url }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{#- Recursive helper: collect all leaf pages under a section. -#}
|
||||
{% macro render_dropdown_items(children) %}
|
||||
{% for child in children %}
|
||||
{% if child.children %}
|
||||
{#- Nested section — render a group label + its children -#}
|
||||
<li class="md-tabs__dropdown-group">
|
||||
<span class="md-tabs__dropdown-label">{{ child.title }}</span>
|
||||
<ul>
|
||||
{{ render_dropdown_items(child.children) }}
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
<a href="{{ child.url | url }}" class="md-tabs__dropdown-link{% if child == page %} md-tabs__dropdown-link--active{% endif %}">
|
||||
{{ child.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
<nav class="md-tabs" aria-label="{{ lang.t('tabs') }}" data-md-component="tabs">
|
||||
<div class="md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
{% for nav_item in nav.items %}
|
||||
{% set class = "md-tabs__item" %}
|
||||
{% if nav_item.active %}
|
||||
{% set class = class ~ " md-tabs__item--active" %}
|
||||
{% endif %}
|
||||
|
||||
{% if nav_item.children %}
|
||||
{#- Section with children — render tab + dropdown -#}
|
||||
{% set class = class ~ " md-tabs__item--dropdown" %}
|
||||
<li class="{{ class }}">
|
||||
<a href="{{ first_leaf_url(nav_item) | trim }}" class="md-tabs__link">
|
||||
{{ nav_item.title }}
|
||||
<span class="md-tabs__dropdown-caret"></span>
|
||||
</a>
|
||||
<ul class="md-tabs__dropdown">
|
||||
{{ render_dropdown_items(nav_item.children) }}
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
{#- Single page — plain tab link -#}
|
||||
<li class="{{ class }}">
|
||||
<a href="{{ nav_item.url | url }}" class="md-tabs__link">
|
||||
{{ nav_item.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
+217
-75
@@ -98,85 +98,227 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* --- Primary nav section collapse fix -----------------------------------------
|
||||
/* --- Navbar dropdown menus ---------------------------------------------------
|
||||
*
|
||||
* Material's native checkbox-based toggle for nav sections (Reference,
|
||||
* Development, Architecture Decisions, etc.) must fully hide children
|
||||
* when unchecked. Without this, the <nav> container retains residual
|
||||
* height from padding/grid-rows even when the checkbox is unchecked,
|
||||
* causing visible whitespace below collapsed section headings.
|
||||
*
|
||||
* DESKTOP ONLY (≥76.25em): On mobile, Material uses a slide-out drawer
|
||||
* with drill-down navigation that depends on nested <nav> elements being
|
||||
* in the render tree. Applying display:none on mobile breaks both the
|
||||
* drill-down animation and scroll-height computation, preventing users
|
||||
* from scrolling to see all nav items.
|
||||
* Custom tabs.html renders top-level sections (Development, Reference, ADRs)
|
||||
* as tabs with hover-activated dropdown menus listing their child pages.
|
||||
* Single-page tabs (Specification, Timeline, FAQ) have no dropdown.
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/* Override Material's overflow/containment on the tabs bar and its list.
|
||||
* Material sets overflow:auto and contain:content which clip absolutely-
|
||||
* positioned children (our dropdown menus). We need visible overflow
|
||||
* so the dropdowns can extend below the tab bar.
|
||||
*
|
||||
* Material's selectors:
|
||||
* .md-tabs { overflow: auto } — (0,1,0)
|
||||
* .md-tabs__list { contain: content; overflow: auto } — (0,1,0)
|
||||
*
|
||||
* Counter-selectors beat them by nesting under .md-header: */
|
||||
.md-header .md-tabs {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.md-header .md-tabs .md-tabs__list {
|
||||
overflow: visible;
|
||||
contain: none;
|
||||
}
|
||||
|
||||
/* Dropdown container — positioned below the tab item */
|
||||
.md-tabs__item--dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md-tabs__dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
min-width: 16rem;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: .4rem 0;
|
||||
background: var(--md-primary-fg-color);
|
||||
border-radius: 0 0 .2rem .2rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* Show dropdown on hover and when the tab is focused within */
|
||||
.md-tabs__item--dropdown:hover > .md-tabs__dropdown,
|
||||
.md-tabs__item--dropdown:focus-within > .md-tabs__dropdown {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Dropdown link items */
|
||||
.md-tabs__dropdown-link {
|
||||
display: block;
|
||||
padding: .4rem 1rem;
|
||||
color: var(--md-primary-bg-color);
|
||||
font-size: .7rem;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
transition: background-color .15s;
|
||||
}
|
||||
|
||||
.md-tabs__dropdown-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.md-tabs__dropdown-link--active {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Nested section groups inside the dropdown */
|
||||
.md-tabs__dropdown-group {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.md-tabs__dropdown-label {
|
||||
display: block;
|
||||
padding: .5rem 1rem .2rem;
|
||||
font-size: .6rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .05em;
|
||||
opacity: .7;
|
||||
color: var(--md-primary-bg-color);
|
||||
}
|
||||
|
||||
.md-tabs__dropdown-group > ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Small caret indicator on tabs that have dropdowns */
|
||||
.md-tabs__dropdown-caret {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: .3rem;
|
||||
vertical-align: middle;
|
||||
border-left: .25rem solid transparent;
|
||||
border-right: .25rem solid transparent;
|
||||
border-top: .25rem solid currentcolor;
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
/* --- Left sidebar: show only the TOC (desktop) --------------------------------
|
||||
*
|
||||
* With navigation.tabs + toc.integrate, the left sidebar contains both the
|
||||
* sub-page navigation for the active tab AND the integrated TOC for the
|
||||
* current page. We hide all non-TOC elements so the left sidebar displays
|
||||
* only the Table of Contents.
|
||||
*
|
||||
* The TOC is always rendered inside <nav class="md-nav--secondary">.
|
||||
* Everything else in the primary sidebar is page navigation.
|
||||
*
|
||||
* DESKTOP ONLY (>= 76.25em) — on mobile, the drawer navigation is used
|
||||
* and must remain fully functional.
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/* Fully hide children of unchecked primary nav sections — desktop only */
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-sidebar--primary .md-nav__item--nested > .md-nav__toggle:not(:checked) ~ .md-nav {
|
||||
/* Hide all top-level nav items in the sidebar — these are the page
|
||||
* links within the active tab section (or the tab items themselves
|
||||
* if the tab has no children). */
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Leaf-page nav items: section-like behaviour ----------------------------
|
||||
*
|
||||
* toc-collapse.js converts ALL leaf-page nav items in the primary sidebar
|
||||
* into section-like DOM structures so every page gets an expand/collapse
|
||||
* chevron. This applies to both top-level single-page items (Specification,
|
||||
* Work Remaining, FAQ) and children within sections (Action CLI, Session
|
||||
* Model, ADR-001, etc.).
|
||||
*
|
||||
* INACTIVE pages are converted into a checkbox + label + nested-nav
|
||||
* structure identical to Material's sections. The page's TOC is
|
||||
* lazy-loaded into the nested nav on first expand.
|
||||
*
|
||||
* ACTIVE pages (the page you're currently viewing) are converted into
|
||||
* an always-expanded, non-collapsible section. The __toc checkbox and
|
||||
* label are hidden, the <a> is replaced with a non-navigating <label>
|
||||
* matching native section heading structure, and the TOC
|
||||
* (md-nav--secondary) is forced visible.
|
||||
* ----------------------------------------------------------------------- */
|
||||
/* But show items on the ancestor path to the current page — these
|
||||
* contain the nested .md-nav--secondary (TOC). */
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* -- Active page item: always-expanded section appearance -----------------
|
||||
*
|
||||
* toc-collapse.js converts the active page item (at any level in the nav
|
||||
* tree) into a non-collapsible section. The __toc checkbox and label are
|
||||
* hidden, the <a> is replaced with a <label> that matches native section
|
||||
* heading structure, and the TOC (md-nav--secondary) is forced visible.
|
||||
*
|
||||
* The marker class .md-nav__item--section-page is added by JS.
|
||||
* ----------------------------------------------------------------------- */
|
||||
/* Within active section wrappers, hide sibling page links (only the
|
||||
* active child with its TOC should be visible).
|
||||
*
|
||||
* IMPORTANT: the :not(.md-nav--secondary) guard prevents this rule
|
||||
* from also matching .md-nav--secondary (the TOC nav), whose <li>
|
||||
* children are plain .md-nav__item without --active and would
|
||||
* otherwise be hidden. */
|
||||
.md-sidebar--primary .md-nav__item--active > .md-nav:not(.md-nav--secondary) > .md-nav__list > .md-nav__item:not(.md-nav__item--active) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Force the TOC always visible (JS also sets inline style as fallback) */
|
||||
.md-nav__item--section-page > .md-nav--secondary {
|
||||
display: block !important;
|
||||
}
|
||||
/* Hide the page-link labels / headings on active items so only the
|
||||
* TOC tree remains visible. This targets:
|
||||
* - Section headings (<label class="md-nav__link">)
|
||||
* - Page links (<a class="md-nav__link">)
|
||||
* - The __toc checkbox and its label
|
||||
* - The section title inside nested navs
|
||||
* but NOT anything inside .md-nav--secondary (the TOC itself). */
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > .md-nav__toggle,
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > label.md-nav__link,
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > a.md-nav__link,
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > .md-nav__container,
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__list > .md-nav__item--active > nav > .md-nav__title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Rotate the chevron to point downward (expanded state), matching the
|
||||
* native rotation rule for checked section toggles. */
|
||||
.md-nav__item--section-page > .md-nav__link > .md-nav__icon::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
/* Within nested sections (level 2+), hide the active page's own link
|
||||
* and __toc machinery, leaving only the .md-nav--secondary TOC. */
|
||||
.md-sidebar--primary .md-nav__item--active > #__toc,
|
||||
.md-sidebar--primary .md-nav__item--active > label[for="__toc"],
|
||||
.md-sidebar--primary .md-nav__item--active > a.md-nav__link,
|
||||
.md-sidebar--primary .md-nav__item--active > .md-nav__container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* -- Lazy-loaded TOC placeholder -- */
|
||||
/* Force the TOC (md-nav--secondary) visible.
|
||||
*
|
||||
* Material's rules that hide or collapse the TOC:
|
||||
*
|
||||
* A) .md-nav--primary .md-nav__link[for=__toc]~.md-nav
|
||||
* → display: none — (0,4,0)
|
||||
*
|
||||
* B) .md-nav__toggle~.md-nav (desktop media query)
|
||||
* → display: grid; opacity: 0; visibility: collapse — (0,2,0)
|
||||
*
|
||||
* Material's integrated rule tries to re-show it:
|
||||
* C) .md-nav--integrated>.md-nav__list>.md-nav__item--active .md-nav--secondary
|
||||
* → display: block; opacity: 1; visibility: visible — (0,4,0)
|
||||
*
|
||||
* A and C tie at (0,4,0), so cascade order decides — and A often
|
||||
* wins. We counter with higher-specificity selectors that
|
||||
* guarantee the TOC is always visible without using !important.
|
||||
*/
|
||||
|
||||
.md-nav__toc-placeholder {
|
||||
padding: .4rem .8rem;
|
||||
font-size: .64rem;
|
||||
opacity: .6;
|
||||
/* Counter rule A — specificity (0,6,0) beats (0,4,0).
|
||||
* Directly negates the sibling-combinator hiding rule. */
|
||||
.md-sidebar--primary .md-nav--primary .md-nav__link[for="__toc"] ~ nav.md-nav--secondary {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* General fallback for integrated mode — specificity (0,5,0)
|
||||
* beats rule B (0,2,0) and ties/beats rule C (0,4,0) by being
|
||||
* loaded later. */
|
||||
.md-sidebar--primary .md-nav--primary.md-nav--integrated .md-nav__item--active .md-nav--secondary {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Remove the top-level sidebar title ("Navigation" label) on desktop
|
||||
* since we no longer show page navigation in the sidebar. */
|
||||
.md-sidebar--primary .md-nav--primary > .md-nav__title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Collapsible TOC in integrated sidebar --- */
|
||||
|
||||
/* Container for the link row: link text + toggle chevron side by side.
|
||||
* Scoped to .md-nav--secondary (active page TOC) and .md-nav--page-toc
|
||||
* (lazy-loaded TOC in converted single-page nav items) so it does not
|
||||
* affect primary nav section headings which use Material's native
|
||||
* <label> + checkbox. */
|
||||
.md-nav--secondary .md-nav__item--nested > .md-nav__link,
|
||||
.md-nav--page-toc .md-nav__item--nested > .md-nav__link {
|
||||
* Scoped to .md-nav--secondary (active page TOC) so it does not affect
|
||||
* primary nav section headings which use Material's native <label> +
|
||||
* checkbox. */
|
||||
.md-nav--secondary .md-nav__item--nested > .md-nav__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -231,13 +373,11 @@
|
||||
* Desktop only — on mobile the TOC is rendered within Material's drawer
|
||||
* and must remain in the render tree for correct scroll behaviour. */
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-nav--secondary .md-nav__item--nested.toc-collapsed > .md-nav,
|
||||
.md-nav--page-toc .md-nav__item--nested.toc-collapsed > .md-nav {
|
||||
.md-nav--secondary .md-nav__item--nested.toc-collapsed > .md-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md-nav--secondary .md-nav__item--nested > .md-nav,
|
||||
.md-nav--page-toc .md-nav__item--nested > .md-nav {
|
||||
.md-nav--secondary .md-nav__item--nested > .md-nav {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@@ -280,15 +420,17 @@
|
||||
|
||||
/* Undo any items hidden by the JS-driven toc-collapsed class that
|
||||
* may have been applied before the mobile guard kicks in (race
|
||||
* condition on resize or orientation change). */
|
||||
.md-sidebar--primary .toc-collapsed > .md-nav {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Undo the section-page forced display set by JS on active pages —
|
||||
* let Material's native mobile styling handle it. */
|
||||
.md-nav__item--section-page > .md-nav--secondary {
|
||||
display: revert !important;
|
||||
* condition on resize or orientation change).
|
||||
*
|
||||
* The desktop hiding rule is:
|
||||
* .md-nav--secondary .md-nav__item--nested.toc-collapsed > .md-nav
|
||||
* { display: none } — (0,3,0), inside a ≥76.25em media query
|
||||
*
|
||||
* This mobile rule applies inside a <76.1875em media query, so the
|
||||
* two never conflict — no !important needed. We bump specificity
|
||||
* slightly for safety against any other rules. */
|
||||
.md-sidebar--primary .md-nav--secondary .toc-collapsed > .md-nav {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,10 @@ nav:
|
||||
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: docs/overrides
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- toc.integrate
|
||||
- toc.follow
|
||||
palette:
|
||||
|
||||
Reference in New Issue
Block a user