From 6f42cf859843ef53f587500f30aada0e64594c37 Mon Sep 17 00:00:00 2001 From: Dominic Tran Date: Fri, 18 Sep 2026 15:24:09 -0500 Subject: [PATCH 1/2] Docs: Restore shared mobile navigation and drawer behavior --- src/components/Header.astro | 2 +- src/components/MobileNavigation.astro | 153 ++++++++++++++++++ src/components/MobileNavigationSections.astro | 29 ++++ src/components/ui/search/SearchTrigger.astro | 2 +- src/components/ui/sidebar/sidebar.client.ts | 2 +- src/layouts/DocsLayout.astro | 120 +------------- src/lib/sidebar-lazy.ts | 22 +++ src/pages/[locale]/index.astro | 4 +- src/pages/index.astro | 4 +- src/styles/globals.css | 16 ++ 10 files changed, 233 insertions(+), 121 deletions(-) create mode 100644 src/components/MobileNavigation.astro create mode 100644 src/components/MobileNavigationSections.astro diff --git a/src/components/Header.astro b/src/components/Header.astro index f62c5b8c2..c91cd6b95 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -50,7 +50,7 @@ const messages = uiStrings(currentLocale);
{!homepage && ( - + {config.title} diff --git a/src/components/MobileNavigation.astro b/src/components/MobileNavigation.astro new file mode 100644 index 000000000..7e1a8bee7 --- /dev/null +++ b/src/components/MobileNavigation.astro @@ -0,0 +1,153 @@ +--- +import LocaleSwitcher from "./LocaleSwitcher.astro"; +import { ThemeToggle } from "./ui/theme-toggle"; +import MobileNavigationSections from "./MobileNavigationSections.astro"; +import Icon from "@cloudflare/nimbus-docs/components/Icon.astro"; +import { uiStrings } from "@/lib/ui-strings.server"; +interface Props { currentLocale?: string; activeLocales?: string[]; } +const messages = uiStrings(Astro.props.currentLocale); +--- + +
+
+ {messages.navigation.navigation} + +
+ +
+
+ + + diff --git a/src/components/MobileNavigationSections.astro b/src/components/MobileNavigationSections.astro new file mode 100644 index 000000000..87f8df9dc --- /dev/null +++ b/src/components/MobileNavigationSections.astro @@ -0,0 +1,29 @@ +--- +import Icon from "@cloudflare/nimbus-docs/components/Icon.astro"; +import { loadGeneratedNavigation } from "@/lib/sidebar-navigation.server"; +import { sectionsFromConfig, mobileSectionsFromConfig } from "@/lib/sidebar-lazy"; +import { localeRouteName } from "@/util/locales"; +interface Props { currentLocale?: string; } +const locale = localeRouteName(Astro.props.currentLocale ?? "en"); +const items = loadGeneratedNavigation(locale).items; +const path = Astro.url.pathname; +const menus = [sectionsFromConfig(items, path), mobileSectionsFromConfig(items, path)]; +--- +
diff --git a/src/components/ui/search/SearchTrigger.astro b/src/components/ui/search/SearchTrigger.astro index 4f325fd49..f5dbd60cc 100644 --- a/src/components/ui/search/SearchTrigger.astro +++ b/src/components/ui/search/SearchTrigger.astro @@ -36,7 +36,7 @@ const messages = uiStrings(currentLocale); CtrlK diff --git a/src/components/ui/sidebar/sidebar.client.ts b/src/components/ui/sidebar/sidebar.client.ts index af2015dd5..2d6dc491e 100644 --- a/src/components/ui/sidebar/sidebar.client.ts +++ b/src/components/ui/sidebar/sidebar.client.ts @@ -31,7 +31,7 @@ function groupKey(group: HTMLElement): string { return labels.join("\u001f"); } -function initSidebar(root: HTMLElement): () => void { +export function initSidebar(root: HTMLElement): () => void { const teardowns: Array<() => void> = []; const persist = root.hasAttribute("data-nb-sidebar-persist"); diff --git a/src/layouts/DocsLayout.astro b/src/layouts/DocsLayout.astro index eab6718a4..c02dc981e 100644 --- a/src/layouts/DocsLayout.astro +++ b/src/layouts/DocsLayout.astro @@ -5,7 +5,7 @@ * Named slots for overrides (all optional, defaults render otherwise): * header, sidebar, toc, page-title, content-footer, pagination */ -import Icon from "@cloudflare/nimbus-docs/components/Icon.astro"; +import MobileNavigation from "@/components/MobileNavigation.astro"; import BaseLayout from "./BaseLayout.astro"; import Header from "@/components/Header.astro"; import AskAiButton from "@/components/AskAiButton.astro"; @@ -320,28 +320,9 @@ const messages = uiStrings(currentLocale); {showSidebar && ( /* Mobile sidebar — native for free focus trap, escape, backdrop. */ - -
-
- {messages.navigation.navigation} - -
- -
-
+ + {hasSidebar ? :
} + )} - diff --git a/src/lib/sidebar-lazy.ts b/src/lib/sidebar-lazy.ts index 1d4ae6296..b85257693 100644 --- a/src/lib/sidebar-lazy.ts +++ b/src/lib/sidebar-lazy.ts @@ -222,3 +222,25 @@ export function sectionsFromConfig(items: ConfigItem[], currentPath: string): Ar isActive: tabIndex === activeTab, })); } + +/** Mobile selectors share the desktop rail's page-to-tab index. */ +export function mobileSectionsFromConfig(items: ConfigItem[], currentPath: string) { + const target = normPath(currentPath); + const index = navigationIndex(items); + const location = index.locations.get(target); + if (!location || location.tabIndex === 0) return []; + const tab = index.tabs[location.tabIndex]; + const groups = location.tabIndex === 2 + ? tab.items.flatMap((item) => "items" in item ? item.items : [item]) + : tab.items; + const destination = (item: ConfigItem): string | undefined => { + const href = "link" in item ? item.link : item.landing; + if (href) return /^(https?:)?\/\//.test(href) ? href : withBase(href); + return "items" in item ? item.items.map(destination).find(Boolean) : undefined; + }; + return groups.map((item) => ({ + label: item.label, + href: destination(item), + isActive: internalLinks([item]).includes(target), + })).filter((item) => item.href); +} diff --git a/src/pages/[locale]/index.astro b/src/pages/[locale]/index.astro index 17ff5c624..588572ebc 100644 --- a/src/pages/[locale]/index.astro +++ b/src/pages/[locale]/index.astro @@ -5,6 +5,7 @@ * existing Mintlify tree until cutover. */ import BaseLayout from "../../layouts/BaseLayout.astro"; +import MobileNavigation from "@/components/MobileNavigation.astro"; import Header from "../../components/Header.astro"; import ActiveHomepage from "@active-homepage"; import { config } from "virtual:nimbus/config"; @@ -29,7 +30,8 @@ if (!["ar", "es", "fr", "ja", "ko", "pt-br", "ru", "zh"].includes(localeCode)) { --- -
+
+
diff --git a/src/pages/index.astro b/src/pages/index.astro index ff7a7037a..08ddbe4a7 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from "../layouts/BaseLayout.astro"; +import MobileNavigation from "@/components/MobileNavigation.astro"; import Header from "../components/Header.astro"; import Homepage from "../generated/homepage/en.jsx"; import { config } from "virtual:nimbus/config"; @@ -7,6 +8,7 @@ import { AVAILABLE_LOCALES } from "../content.config"; --- -
+
+
diff --git a/src/styles/globals.css b/src/styles/globals.css index 77c23f7a6..543cd2263 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -2148,6 +2148,22 @@ body:not(:has([data-mobile-sidebar])) [data-menu-btn] { display: none; } + /* The drawer already supplies horizontal padding around its cloned rail. */ + [data-mobile-sidebar] .ch-sidebar-tools { + height: auto; + padding: 0 0 12px; + } + + [data-mobile-sidebar] .ch-sidebar-filter { + padding: 0; + } + + [data-mobile-sidebar] .ch-sidebar-scroll { + overflow: visible; + padding: 0 0 16px; + } + + .ch-document-grid { display: block; min-height: 0; From 8e6b298ca1f1704a24b8c69710cd25a727afafb4 Mon Sep 17 00:00:00 2001 From: Dominic Tran Date: Fri, 18 Sep 2026 15:30:07 -0500 Subject: [PATCH 2/2] Docs: Reset cloned sidebar disclosure runtime attributes --- src/components/MobileNavigation.astro | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/MobileNavigation.astro b/src/components/MobileNavigation.astro index 7e1a8bee7..73feecce9 100644 --- a/src/components/MobileNavigation.astro +++ b/src/components/MobileNavigation.astro @@ -47,6 +47,12 @@ const messages = uiStrings(Astro.props.currentLocale); target.appendChild(clone); } target.querySelectorAll("[data-nb-sidebar-persist]").forEach((element) => element.removeAttribute("data-nb-sidebar-persist")); + // Cloning copies runtime IDs and binding markers, but not listeners. + // Let Nimbus create fresh ARIA targets and bind each copied disclosure. + target.querySelectorAll("[data-nb-collapsible]").forEach((element) => { + delete element.dataset.chBound; + }); + target.querySelectorAll("[data-nb-collapsible-content]").forEach((element) => element.removeAttribute("id")); bindCollapsibles(target); const cleanups = [...target.querySelectorAll("[data-nb-sidebar]")].map(initSidebar); return () => cleanups.forEach((cleanup) => cleanup());