fix: improve mobile navigation and layouts on docs site#3
fix: improve mobile navigation and layouts on docs site#3michaelmcnees wants to merge 2 commits into
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…achability Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the docs site’s mobile UX by introducing a fixed bottom navigation bar, adjusting docs sidebar behavior for mobile, and applying mobile-specific typography/spacing tweaks to prevent layout overflow and improve readability.
Changes:
- Replaced the previous mobile hamburger/menu approach with a fixed bottom navigation bar (with docs-specific menu toggle).
- Updated docs layout/sidebar to accommodate the bottom bar (extra bottom spacing and mobile drawer sizing).
- Added mobile prose CSS adjustments (heading sizes, code block sizing) and attempted small-screen table overflow handling.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
site/src/styles/global.css |
Adds mobile typography tweaks and new table-wrapper styling intended for small-screen table overflow. |
site/src/pages/index.astro |
Adds bottom padding to prevent home page content being obscured by the fixed bottom nav on mobile. |
site/src/layouts/Docs.astro |
Adds bottom padding (mobile) and overflow-x-hidden to mitigate mobile overflow issues in docs layout. |
site/src/components/Nav.astro |
Implements the fixed mobile bottom navigation and JS toggle for the docs mobile sidebar. |
site/src/components/DocsSidebar.astro |
Adjusts the mobile sidebar drawer height and updates overlay/link close behavior. |
Comments suppressed due to low confidence (1)
site/src/components/DocsSidebar.astro:201
initSidebar()replaces#sidebar-overlaywith a cloned node, butcloseSidebar()still references the originaloverlayconstant. As a result, clicking the new overlay will callcloseSidebar()and attempt to hide the old (detached) overlay, leaving the visible overlay in place. Either avoid cloning/replacing the overlay, or update the reference used bycloseSidebar()to the newly inserted node before attaching listeners.
<script is:inline>
function initSidebar() {
const mobileSidebar = document.getElementById('docs-sidebar-mobile');
const overlay = document.getElementById('sidebar-overlay');
if (!mobileSidebar || !overlay) return;
function closeSidebar() {
mobileSidebar.classList.add('-translate-x-full');
overlay.classList.add('hidden');
}
const newOverlay = overlay.cloneNode(true);
overlay.parentNode?.replaceChild(newOverlay, overlay);
newOverlay.addEventListener('click', closeSidebar);
mobileSidebar.querySelectorAll('a').forEach((a) => {
a.addEventListener('click', closeSidebar);
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const btn = document.getElementById('bottom-nav-docs-btn'); | ||
| const docsSidebar = document.getElementById('docs-sidebar-mobile'); | ||
| const overlay = document.getElementById('sidebar-overlay'); | ||
| if (!btn || !docsSidebar || !overlay) return; | ||
|
|
||
| // Remove old listeners by cloning | ||
| const newBtn = btn.cloneNode(true); | ||
| btn.parentNode?.replaceChild(newBtn, btn); | ||
| newBtn.addEventListener('click', () => menu.classList.toggle('hidden')); | ||
|
|
||
| newBtn.addEventListener('click', () => { | ||
| const isOpen = !docsSidebar.classList.contains('-translate-x-full'); | ||
| if (isOpen) { | ||
| docsSidebar.classList.add('-translate-x-full'); | ||
| overlay.classList.add('hidden'); | ||
| } else { | ||
| docsSidebar.classList.remove('-translate-x-full'); | ||
| overlay.classList.remove('hidden'); | ||
| } |
There was a problem hiding this comment.
initNav() captures a reference to #sidebar-overlay, but DocsSidebar.astro replaces that element via cloneNode/replaceChild. That leaves this handler toggling classes on a detached node, so the overlay (and menu close behavior) can break. Prefer not replacing #sidebar-overlay at all, or re-query the current overlay element inside the click handler (or ensure only one component owns overlay event binding).
| <Base title="Mantle — Headless AI Workflow Automation"> | ||
| <Nav isHome={true} /> | ||
| <main id="main-content"> | ||
| <main id="main-content" class="pb-16 md:pb-0"> |
There was a problem hiding this comment.
The new bottom padding (pb-16) doesn’t account for env(safe-area-inset-bottom). On devices with a home indicator, the bottom nav height becomes h-14 + safe-area, so 4rem padding may still allow content to be obscured. Consider using a calc that includes the safe-area inset (e.g., pb-[calc(4rem+env(safe-area-inset-bottom))]) or a shared CSS variable derived from the bottom-nav height.
| <main id="main-content" class="pb-16 md:pb-0"> | |
| <main id="main-content" class="pb-[calc(4rem+env(safe-area-inset-bottom))] md:pb-0"> |
| </button> | ||
|
|
||
| <div class="mx-auto max-w-[1280px] px-4 sm:px-6 overflow-x-hidden"> | ||
| <div class="flex min-h-screen pt-16 pb-16 md:pb-0"> |
There was a problem hiding this comment.
The pb-16 added for the mobile bottom nav doesn’t include env(safe-area-inset-bottom), so on iOS the last content can still sit under the nav (nav adds safe-area padding in Nav.astro). Consider using padding that includes the safe-area inset (or a shared CSS var) so the docs content always clears the bottom bar.
| <div class="flex min-h-screen pt-16 pb-16 md:pb-0"> | |
| <div class="flex min-h-screen pt-16 pb-[calc(4rem+env(safe-area-inset-bottom))] md:pb-0"> |
|
|
||
| <!-- Mobile sidebar drawer --> | ||
| <aside id="docs-sidebar-mobile" class="fixed top-16 left-0 bottom-0 z-50 w-72 bg-surface-container-lowest border-r border-outline-variant transform -translate-x-full transition-transform duration-200 lg:hidden overflow-y-auto"> | ||
| <aside id="docs-sidebar-mobile" class="fixed top-16 left-0 bottom-14 md:bottom-0 z-50 w-72 bg-surface-container-lowest border-r border-outline-variant transform -translate-x-full transition-transform duration-200 lg:hidden overflow-y-auto"> |
There was a problem hiding this comment.
bottom-14 reserves space for a 56px bottom nav, but the nav also adds padding-bottom: env(safe-area-inset-bottom). On devices with a bottom safe-area inset, the sidebar can overlap the bottom nav/home-indicator region. Consider including the safe-area inset in the sidebar’s bottom offset (or add equivalent padding to the sidebar) to prevent content from being hidden.
| <aside id="docs-sidebar-mobile" class="fixed top-16 left-0 bottom-14 md:bottom-0 z-50 w-72 bg-surface-container-lowest border-r border-outline-variant transform -translate-x-full transition-transform duration-200 lg:hidden overflow-y-auto"> | |
| <aside id="docs-sidebar-mobile" class="fixed top-16 left-0 bottom-[calc(3.5rem+env(safe-area-inset-bottom))] md:bottom-0 z-50 w-72 bg-surface-container-lowest border-r border-outline-variant transform -translate-x-full transition-transform duration-200 lg:hidden overflow-y-auto"> |
| /* Tables — horizontally scrollable on small screens */ | ||
| .prose .table-wrapper { | ||
| overflow-x: auto; | ||
| -webkit-overflow-scrolling: touch; | ||
| margin-top: 1.5em; | ||
| } | ||
|
|
||
| .prose .table-wrapper table { | ||
| margin-top: 0; | ||
| } |
There was a problem hiding this comment.
The new .prose .table-wrapper styles won’t have any effect unless tables are actually wrapped in an element with class="table-wrapper" (search in site/src shows no usage). If the goal is to make markdown tables scroll on mobile, consider applying overflow-x: auto/display: block directly to .prose table under a small-screen media query or adding a markdown/rehype transform that wraps generated tables.
| Menu | ||
| </button> | ||
|
|
||
| <div class="mx-auto max-w-[1280px] px-4 sm:px-6 overflow-x-hidden"> |
There was a problem hiding this comment.
overflow-x-hidden on the docs container can clip any horizontally overflowing content (notably wide markdown tables). Since the new table-scrolling styles currently target a .table-wrapper that isn’t present, this may result in table content being cut off with no way to scroll. Consider removing overflow-x-hidden or ensuring tables/code blocks have an internal horizontal scroll container before hiding overflow on the page wrapper.
| <div class="mx-auto max-w-[1280px] px-4 sm:px-6 overflow-x-hidden"> | |
| <div class="mx-auto max-w-[1280px] px-4 sm:px-6"> |
| <!-- Mobile bottom navigation bar --> | ||
| <nav id="mobile-bottom-nav" class="fixed bottom-0 left-0 right-0 z-50 md:hidden border-t border-outline-variant bg-surface/95 backdrop-blur-md"> | ||
| <div class="flex items-center justify-around h-14 px-2"> | ||
| <a href="/" class:list={["bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] transition-colors", isHome ? "text-primary" : "text-on-surface-variant hover:text-on-surface"]}> | ||
| <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955a1.126 1.126 0 011.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/></svg> | ||
| <span>Home</span> | ||
| </a> | ||
| {isDocs && ( | ||
| <button id="bottom-nav-docs-btn" class="bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] text-on-surface-variant hover:text-on-surface transition-colors" aria-label="Documentation menu"> | ||
| <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/></svg> | ||
| <span>Menu</span> | ||
| </button> | ||
| )} | ||
| <a href="/docs/getting-started" class:list={["bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] transition-colors", isDocs ? "text-primary" : "text-on-surface-variant hover:text-on-surface"]}> | ||
| <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"/></svg> | ||
| <span>Docs</span> | ||
| </a> | ||
| <a href="https://github.com/dvflw/mantle" class="bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] text-on-surface-variant hover:text-on-surface transition-colors" aria-label="GitHub"> | ||
| <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg> | ||
| <span>GitHub</span> | ||
| </a> |
There was a problem hiding this comment.
PR description mentions the mobile bottom bar includes “home, menu, and search” actions, but the implemented bottom nav has Home, Menu (docs only), Docs, and GitHub — no search action/link. Either update the PR description to match what shipped, or add the intended search affordance to the bottom nav.
Changes
Why bottom nav?
Mobile bottom navigation follows standard thumb-zone conventions for easier one-handed use, which matters for a developer docs site where users often reference docs while working.