Fix MkDocs search results not displaying with custom header

Use inline JS styles (applySearchLayout) instead of CSS-only approach
for search panel layout - fixes Firefox compatibility where cross-origin
Material stylesheets override !important rules. Adds explicit height,
flex layout, z-index, and background on search elements. Also fixes
click-to-exit by deferring DOM queries to DOMContentLoaded. Syncs
header-builder.service.ts with main.html changes.

Bunker Admin
This commit is contained in:
2026-03-11 16:51:55 -06:00
parent b136396f3b
commit 12734aca16
96 changed files with 23020 additions and 5701 deletions

View File

@@ -626,8 +626,6 @@ class HeaderBuilderService {
</button>
</div>
<div class="cm-header-nav__mobile-links">
${mobileLinks}
<div class="cm-header-nav__mobile-divider"></div>
<label for="__search" class="cm-header-nav__mobile-link" style="cursor:pointer">
<span class="material-icons-outlined">search</span>
<span>Search</span>
@@ -641,6 +639,8 @@ class HeaderBuilderService {
<span>Docs Navigation</span>
</button>
<div class="cm-header-nav__mobile-divider"></div>
${mobileLinks}
<div class="cm-header-nav__mobile-divider"></div>
<a href="#" data-path="/login" class="cm-header-nav__mobile-link" id="cm-mobile-signin-link">
<span class="material-icons-outlined">login</span>
<span>Sign In</span>
@@ -778,6 +778,7 @@ class HeaderBuilderService {
// Close custom drawer when search label is clicked on mobile + auto-focus input
document.querySelectorAll('label[for="__search"]').forEach(function(el) {
el.addEventListener('click', function() {
if (el.classList.contains('md-search__overlay')) return; // overlay has its own handler
closeDrawer();
setTimeout(function() {
var input = document.querySelector('.md-search__input');
@@ -785,29 +786,154 @@ class HeaderBuilderService {
}, 150);
});
});
// Search activation: mirror checkbox state as a body class for CSS targeting.
// This avoids reliance on the ~ sibling combinator (fragile with template blocks).
var searchToggle = document.getElementById('__search');
if (searchToggle) {
function syncSearchClass() {
document.body.classList.toggle('cm-search-active', searchToggle.checked);
// Search activation: Material may open search via checkbox OR by focusing the
// input directly (varies by version). Detect both and mirror as body class.
// NOTE: search DOM elements render AFTER the announce block in the template,
// so we must defer element queries until DOMContentLoaded.
var searchToggle = null;
var searchInput = null;
// Apply search layout inline styles (CSS-in-stylesheet is unreliable due to
// cross-origin Material stylesheets overriding !important rules)
function applySearchLayout(active) {
var inner = document.querySelector('.md-search__inner');
var output = document.querySelector('.md-search__output');
var scrollwrap = document.querySelector('.md-search__scrollwrap');
if (!inner) return;
var isDesktop = window.matchMedia('(min-width: 60em)').matches;
if (active) {
inner.style.setProperty('display', 'flex', 'important');
inner.style.setProperty('flex-direction', 'column', 'important');
inner.style.setProperty('overflow', 'hidden', 'important');
// Firefox needs explicit height (not just max-height) for flex children to grow
if (isDesktop) {
inner.style.setProperty('height', 'calc(100vh - 64px)', 'important');
}
if (output) {
output.style.setProperty('position', 'relative', 'important');
output.style.setProperty('flex', '1 1 0px', 'important');
output.style.setProperty('min-height', '0', 'important');
output.style.setProperty('display', 'flex', 'important');
output.style.setProperty('flex-direction', 'column', 'important');
output.style.setProperty('overflow', 'hidden', 'important');
output.style.setProperty('width', '100%', 'important');
}
if (scrollwrap) {
scrollwrap.style.setProperty('max-height', 'none', 'important');
scrollwrap.style.setProperty('flex', '1 1 0px', 'important');
scrollwrap.style.setProperty('min-height', '0', 'important');
scrollwrap.style.setProperty('overflow-y', 'auto', 'important');
}
// Force search result elements visible + ensure proper stacking (Firefox)
var resultList = document.querySelector('.md-search-result__list');
if (resultList) {
resultList.style.setProperty('display', 'block', 'important');
resultList.style.setProperty('visibility', 'visible', 'important');
resultList.style.setProperty('opacity', '1', 'important');
resultList.style.setProperty('max-height', 'none', 'important');
resultList.style.setProperty('overflow', 'visible', 'important');
resultList.style.setProperty('color', 'var(--md-default-fg-color)', 'important');
}
var resultContainer = document.querySelector('.md-search-result');
if (resultContainer) {
resultContainer.style.setProperty('display', 'block', 'important');
resultContainer.style.setProperty('visibility', 'visible', 'important');
resultContainer.style.setProperty('opacity', '1', 'important');
}
// Ensure scrollwrap has z-index above overlay
if (scrollwrap) {
scrollwrap.style.setProperty('position', 'relative', 'important');
scrollwrap.style.setProperty('z-index', '1', 'important');
scrollwrap.style.setProperty('background', 'var(--md-default-bg-color)', 'important');
}
} else {
inner.style.removeProperty('display');
inner.style.removeProperty('flex-direction');
inner.style.removeProperty('overflow');
inner.style.removeProperty('height');
if (output) {
output.style.removeProperty('position');
output.style.removeProperty('flex');
output.style.removeProperty('min-height');
output.style.removeProperty('display');
output.style.removeProperty('flex-direction');
output.style.removeProperty('overflow');
output.style.removeProperty('width');
}
if (scrollwrap) {
scrollwrap.style.removeProperty('max-height');
scrollwrap.style.removeProperty('flex');
scrollwrap.style.removeProperty('min-height');
scrollwrap.style.removeProperty('overflow-y');
scrollwrap.style.removeProperty('position');
scrollwrap.style.removeProperty('z-index');
scrollwrap.style.removeProperty('background');
}
var resultList = document.querySelector('.md-search-result__list');
if (resultList) resultList.removeAttribute('style');
var resultContainer = document.querySelector('.md-search-result');
if (resultContainer) resultContainer.removeAttribute('style');
}
searchToggle.addEventListener('change', syncSearchClass);
syncSearchClass();
// Click-outside to dismiss search
document.addEventListener('click', function(e) {
if (!searchToggle.checked) return;
var panel = document.querySelector('.md-search__inner');
if (panel && panel.contains(e.target)) return;
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
searchToggle.checked = false;
syncSearchClass();
});
// Also sync on Escape key (Material toggles checkbox via JS)
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') setTimeout(syncSearchClass, 50);
});
}
function activateSearch() {
document.body.classList.add('cm-search-active');
if (searchToggle) searchToggle.checked = true;
applySearchLayout(true);
}
function deactivateSearch() {
document.body.classList.remove('cm-search-active');
if (searchToggle) searchToggle.checked = false;
if (searchInput) searchInput.blur();
applySearchLayout(false);
}
function isSearchActive() {
return document.body.classList.contains('cm-search-active');
}
// Custom search labels in the cm-header-nav (these exist now, in announce block)
document.querySelectorAll('label[for="__search"]').forEach(function(lbl) {
lbl.addEventListener('click', function() {
if (lbl.classList.contains('md-search__overlay')) return;
setTimeout(function() { activateSearch(); if (searchInput) searchInput.focus(); }, 50);
});
});
// Deferred bindings: attach handlers to search elements once they exist in the DOM
document.addEventListener('DOMContentLoaded', function() {
searchToggle = document.getElementById('__search');
searchInput = document.querySelector('.md-search__input');
// Detect search open via input focus
if (searchInput) {
searchInput.addEventListener('focus', activateSearch);
}
// Detect search open via checkbox
if (searchToggle) {
searchToggle.addEventListener('change', function() {
if (searchToggle.checked) activateSearch(); else deactivateSearch();
});
}
// Click on overlay (md-search__overlay label) to dismiss search
var searchOverlay = document.querySelector('.md-search__overlay');
if (searchOverlay) {
searchOverlay.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
if (isSearchActive()) deactivateSearch();
});
}
});
// Click-outside to dismiss search (on document, works immediately)
document.addEventListener('mousedown', function(e) {
if (!isSearchActive()) return;
var panel = document.querySelector('.md-search__inner');
if (panel && panel.contains(e.target)) return;
// Let the overlay's own click handler deal with it
if (e.target.closest && e.target.closest('.md-search__overlay')) return;
if (e.target.closest && e.target.closest('label[for="__search"]')) return;
if (e.target.closest && e.target.closest('.cm-header-nav__utility')) return;
deactivateSearch();
});
// Escape key to dismiss
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && isSearchActive()) setTimeout(deactivateSearch, 50);
});
// Init palette icon + observe changes
setTimeout(updatePaletteIcon, 100);
new MutationObserver(function() { updatePaletteIcon(); })
@@ -816,12 +942,15 @@ class HeaderBuilderService {
</script>
<style>
.md-banner {
background: ${escapeHtml(backgroundColor)} !important;
background: transparent !important;
color: ${escapeHtml(textColor)} !important;
padding: 0 !important;
margin: 0 !important;
overflow: visible !important;
border: none !important;
box-shadow: none !important;
position: relative;
z-index: 301;
}
.md-banner__inner {
overflow: visible !important;
@@ -1054,7 +1183,11 @@ class HeaderBuilderService {
.cm-header-nav__hamburger { display: block; }
.cm-header-nav__dropdown-menu { display: none !important; }
}
/* Hidden Material header — stays at 0 height normally */
/* Sidebar sticky offset = 0 since blue header scrolls away */
:root {
--md-header-height: 0px;
}
/* Hidden Material header — keeps search anchored near tabs */
.md-header--cm-hidden {
height: 0 !important;
min-height: 0 !important;
@@ -1064,24 +1197,33 @@ class HeaderBuilderService {
overflow: visible !important;
background: transparent !important;
box-shadow: none !important;
position: sticky;
top: 0;
z-index: 200;
}
/* === DESKTOP SEARCH (>= 60em / 960px) === */
@media screen and (min-width: 60em) {
/* When search is active, make the search panel a fixed dropdown below custom header */
/* Fixed dropdown panel — layout (flex) applied via JS inline styles */
body.cm-search-active .md-header--cm-hidden .md-search__inner {
position: fixed !important;
top: 56px !important;
top: 48px !important;
right: 16px !important;
left: auto !important;
width: min(34rem, calc(100vw - 32px)) !important;
max-height: calc(100vh - 64px) !important;
background: var(--md-default-bg-color) !important;
border-radius: 0 0 8px 8px !important;
box-shadow: 0 4px 24px rgba(0,0,0,0.25) !important;
z-index: 300 !important;
opacity: 1 !important;
transform: none !important;
visibility: visible !important;
pointer-events: auto !important;
clip-path: none !important;
}
/* Dark overlay behind search panel */
/* Dark overlay behind search panel — catches clicks to dismiss */
body.cm-search-active .md-header--cm-hidden .md-search__overlay {
position: fixed !important;
top: 0 !important;
@@ -1093,12 +1235,14 @@ class HeaderBuilderService {
z-index: 299 !important;
border-radius: 0 !important;
transform: none !important;
cursor: default !important;
pointer-events: auto !important;
}
}
/* === MOBILE SEARCH (< 60em / 960px) === */
@media screen and (max-width: 59.984375em) {
/* Full-screen search takeover on mobile */
/* Full-screen search takeover — layout (flex) applied via JS inline styles */
body.cm-search-active .md-header--cm-hidden .md-search__inner {
position: fixed !important;
top: 0 !important;
@@ -1108,26 +1252,42 @@ class HeaderBuilderService {
width: 100% !important;
height: 100% !important;
opacity: 1 !important;
overflow: visible !important;
transform: none !important;
visibility: visible !important;
pointer-events: auto !important;
z-index: 300 !important;
background: var(--md-default-bg-color) !important;
clip-path: none !important;
}
}
/* Force search results to show when active (both breakpoints) */
/* Force search elements visible when active (layout handled by JS inline styles) */
body.cm-search-active .md-header--cm-hidden .md-search {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
overflow: visible !important;
}
body.cm-search-active .md-header--cm-hidden .md-search__output {
opacity: 1 !important;
}
body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
max-height: 75vh !important;
visibility: visible !important;
clip-path: none !important;
transform: none !important;
}
.cm-palette-container {
height: 0 !important;
overflow: hidden !important;
}
/* Hide Material tabs — custom header covers navigation */
.md-tabs { display: none !important; }
/* Material tabs: sticky at viewport top when blue header scrolls away */
.md-tabs {
position: sticky;
top: 0;
z-index: 99;
}
/* On mobile, hide tabs (sidebar provides navigation) */
@media (max-width: 768px) {
.md-tabs { display: none; }
}
/* Utility icon styling */
.cm-header-nav__utility {
background: none;
@@ -1174,7 +1334,11 @@ body.cm-search-active .md-header--cm-hidden .md-search__scrollwrap {
</header>
{% endblock %}
{% block tabs %}{% endblock %}
{% block tabs %}
{% if "navigation.tabs" in config.theme.features %}
{% include "partials/tabs.html" %}
{% endif %}
{% endblock %}
`;
}