Improve mobile chrome for the slatehtml docs shell.

Show a top bar with menu/logo/title when the nav drawer is active, hide the outline at the same breakpoint, fix the FA hamburger icon, and use a message glyph for Discord socials.
This commit is contained in:
2026-08-02 21:30:35 +10:00
parent 893d0f02be
commit f4b40e0d97
2 changed files with 92 additions and 35 deletions

View File

@@ -52,6 +52,7 @@ export class DocsApp {
articleScroll: () => host.querySelector("[data-docs-article-scroll]"), articleScroll: () => host.querySelector("[data-docs-article-scroll]"),
siteName: () => host.querySelector("[data-docs-site-name]"), siteName: () => host.querySelector("[data-docs-site-name]"),
authorRole: () => host.querySelector("[data-docs-author-role]"), authorRole: () => host.querySelector("[data-docs-author-role]"),
pageTitle: () => host.querySelector("[data-docs-page-title]"),
socials: () => host.querySelector("[data-docs-socials]"), socials: () => host.querySelector("[data-docs-socials]"),
search: () => host.querySelector("[data-docs-search]"), search: () => host.querySelector("[data-docs-search]"),
searchPanel: () => host.querySelector("[data-docs-search-panel]"), searchPanel: () => host.querySelector("[data-docs-search-panel]"),
@@ -156,16 +157,23 @@ export class DocsApp {
if (siteEl) siteEl.setAttribute("text", site); if (siteEl) siteEl.setAttribute("text", site);
document.title = site; document.title = site;
const logo = this.host.querySelector("[data-docs-logo]"); const logoUrl = (data.metadata?.logo || this.host.getAttribute("logo") || "./img/logo.png").trim();
if (logo) { for (const logo of this.host.querySelectorAll("[data-docs-logo], [data-docs-logo-top]")) {
logo.setAttribute("alt", site); logo.setAttribute("alt", site);
const logoUrl = (data.metadata?.logo || this.host.getAttribute("logo") || "./img/logo.png").trim();
if (logoUrl) { if (logoUrl) {
logo.setAttribute("src", logoUrl); logo.setAttribute("src", logoUrl);
logo.removeAttribute("hidden"); logo.removeAttribute("hidden");
siteEl?.setAttribute("hidden", ""); } else {
logo.setAttribute("hidden", "");
} }
} }
if (logoUrl) siteEl?.setAttribute("hidden", "");
else siteEl?.removeAttribute("hidden");
const pageTitle = this.$.pageTitle();
if (pageTitle && !pageTitle.getAttribute("text")) {
pageTitle.setAttribute("text", site);
}
const role = this.$.authorRole(); const role = this.$.authorRole();
if (role) { if (role) {
@@ -176,7 +184,11 @@ export class DocsApp {
const socials = this.$.socials(); const socials = this.$.socials();
if (socials) { if (socials) {
const entries = (data.author?.socials || []).map((s) => { const entries = (data.author?.socials || []).map((s) => {
const icon = faClassToIconName(s.icon); let icon = faClassToIconName(s.icon);
// Prefer a generic message glyph over the Discord brand mark.
if (/discord/i.test(String(s.icon || "")) || /discord/i.test(String(s.url || ""))) {
icon = "fas:message";
}
const label = shortSocialLabel(s.title || s.url || "link"); const label = shortSocialLabel(s.title || s.url || "link");
// value carries the URL so selection can open it // value carries the URL so selection can open it
return `${encodeURIComponent(s.url)}|${label}|${icon}`; return `${encodeURIComponent(s.url)}|${label}|${icon}`;
@@ -238,6 +250,7 @@ export class DocsApp {
md.setAttribute("content", content); md.setAttribute("content", content);
} }
document.title = `${title} · ${this.indexData?.metadata?.site_name || "Docs"}`; document.title = `${title} · ${this.indexData?.metadata?.site_name || "Docs"}`;
this.$.pageTitle()?.setAttribute("text", title || "Documentation");
const fromPath = findSlugByPath(this.indexData?.documents || [], path); const fromPath = findSlugByPath(this.indexData?.documents || [], path);
this.$.nav()?.setAttribute("selected", fromPath || slug || ""); this.$.nav()?.setAttribute("selected", fromPath || slug || "");

View File

@@ -19,7 +19,7 @@
tabindex="0" tabindex="0"
aria-label="Close navigation" aria-label="Close navigation"
> >
<slate-icon name="menu" size="16"></slate-icon> <slate-icon name="bars" size="16"></slate-icon>
</border> </border>
<verticalbox gap="10" fill min-width="0" halign="center"> <verticalbox gap="10" fill min-width="0" halign="center">
<img class="docs-viewer-logo-img" data-docs-logo src="./img/logo.png" alt="" /> <img class="docs-viewer-logo-img" data-docs-logo src="./img/logo.png" alt="" />
@@ -55,6 +55,27 @@
<canvaspanel class="docs-viewer-main" fill min-width="0" min-height="0"> <canvaspanel class="docs-viewer-main" fill min-width="0" min-height="0">
<verticalbox anchors="fill" top="0" left="0" right="0" bottom="0" gap="0"> <verticalbox anchors="fill" top="0" left="0" right="0" bottom="0" gap="0">
<horizontalbox
class="docs-viewer-topbar"
gap="10"
valign="center"
padding="0 12"
height="48"
>
<border
class="docs-viewer-topbar-menu"
sidebar="docs-viewer-sidebar"
padding="6 8"
role="button"
tabindex="0"
aria-label="Open navigation"
>
<slate-icon name="bars" size="16"></slate-icon>
</border>
<img class="docs-viewer-topbar-logo" data-docs-logo-top src="./img/logo.png" alt="" />
<textblock class="docs-viewer-topbar-divider" text="/"></textblock>
<textblock class="docs-viewer-topbar-title" data-docs-page-title text="Documentation" fill></textblock>
</horizontalbox>
<border class="docs-viewer-progress-wrap" padding="0" hidden data-docs-progress> <border class="docs-viewer-progress-wrap" padding="0" hidden data-docs-progress>
<slate-progress indeterminate></slate-progress> <slate-progress indeterminate></slate-progress>
</border> </border>
@@ -73,26 +94,12 @@
</verticalbox> </verticalbox>
</scrollbox> </scrollbox>
</verticalbox> </verticalbox>
<border
class="docs-viewer-menu-fab"
sidebar="docs-viewer-sidebar"
anchors="top-left"
top="12"
left="12"
padding="8"
role="button"
tabindex="0"
aria-label="Open navigation"
>
<slate-icon name="menu" size="18"></slate-icon>
</border>
</canvaspanel> </canvaspanel>
<slate-side-bar <slate-side-bar
class="docs-viewer-outline-rail" class="docs-viewer-outline-rail"
width="240" width="240"
collapse-at="1100" collapse-at="1000"
placement="right" placement="right"
mode="rail" mode="rail"
> >
@@ -151,18 +158,49 @@ self:has(#docs-viewer-sidebar[layout="drawer"][open]) .docs-viewer-menu {
display: block; /* umc-layout-ok */ display: block; /* umc-layout-ok */
} }
.docs-viewer-menu-fab { .docs-viewer-topbar {
--widget-background: var(--panel); flex: 0 0 auto; /* umc-layout-ok */
--widget-border: 1px solid var(--line); display: none; /* umc-layout-ok — mobile / drawer only */
--widget-radius: 8px; background: var(--panel);
cursor: pointer; border-bottom: 1px solid var(--line);
z-index: 15; /* umc-layout-ok */ box-sizing: border-box;
display: none; /* umc-layout-ok */
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
} }
self:has(#docs-viewer-sidebar[layout="drawer"]:not([open])) .docs-viewer-menu-fab { self:has(#docs-viewer-sidebar[layout="drawer"]) .docs-viewer-topbar {
display: flex; /* umc-layout-ok */
}
.docs-viewer-topbar-menu {
--widget-background: transparent;
--widget-border: none;
cursor: pointer;
flex: 0 0 auto; /* umc-layout-ok */
}
.docs-viewer-topbar-logo {
display: block; /* umc-layout-ok */ display: block; /* umc-layout-ok */
height: 24px; /* umc-layout-ok — OG .brand-logo */
width: auto; /* umc-layout-ok */
object-fit: contain;
flex: 0 0 auto; /* umc-layout-ok */
}
.docs-viewer-topbar-logo[hidden] {
display: none !important;
}
.docs-viewer-topbar-divider {
color: var(--muted);
font-size: 0.9rem;
flex: 0 0 auto; /* umc-layout-ok */
}
.docs-viewer-topbar-title {
font-size: 0.9rem;
min-width: 0; /* umc-layout-ok */
overflow: hidden; /* umc-layout-ok */
text-overflow: ellipsis;
white-space: nowrap;
} }
.docs-viewer-logo { .docs-viewer-logo {
@@ -193,6 +231,12 @@ self:has(#docs-viewer-sidebar[layout="drawer"]:not([open])) .docs-viewer-menu-fa
background: var(--bg, #1a1a1a); background: var(--bg, #1a1a1a);
} }
/* Drop the outline with the nav drawer (same breakpoint) — no outline drawer. */
self:has(#docs-viewer-sidebar[layout="drawer"]) .docs-viewer-outline-rail,
.docs-viewer-outline-rail[layout="drawer"] {
display: none !important;
}
.docs-viewer-outline-rail .slate-sidebar-panel { .docs-viewer-outline-rail .slate-sidebar-panel {
--widget-background: var(--bg, #1a1a1a); --widget-background: var(--bg, #1a1a1a);
background: var(--bg, #1a1a1a); background: var(--bg, #1a1a1a);
@@ -316,7 +360,7 @@ self:has(#docs-viewer-sidebar[layout="drawer"]:not([open])) .docs-viewer-menu-fa
.docs-viewer-nav, .docs-viewer-nav,
.docs-viewer-outline-rail, .docs-viewer-outline-rail,
.docs-viewer-menu, .docs-viewer-menu,
.docs-viewer-menu-fab, .docs-viewer-topbar,
.docs-viewer-search-block, .docs-viewer-search-block,
.docs-viewer-footer, .docs-viewer-footer,
.docs-viewer-progress-wrap { .docs-viewer-progress-wrap {
@@ -352,18 +396,18 @@ export default defineUmc({
SynchronizeProperties(el) { SynchronizeProperties(el) {
const logoUrl = (el.getAttribute("logo") || "./img/logo.png").trim(); const logoUrl = (el.getAttribute("logo") || "./img/logo.png").trim();
const img = el.querySelector("[data-docs-logo]"); const imgs = el.querySelectorAll("[data-docs-logo], [data-docs-logo-top]");
const text = el.querySelector("[data-docs-site-name]"); const text = el.querySelector("[data-docs-site-name]");
if (img) { for (const img of imgs) {
if (logoUrl) { if (logoUrl) {
img.setAttribute("src", logoUrl); img.setAttribute("src", logoUrl);
img.removeAttribute("hidden"); img.removeAttribute("hidden");
text?.setAttribute("hidden", "");
} else { } else {
img.setAttribute("hidden", ""); img.setAttribute("hidden", "");
text?.removeAttribute("hidden");
} }
} }
if (logoUrl) text?.setAttribute("hidden", "");
else text?.removeAttribute("hidden");
}, },
}); });