From 3e9b6cd8d736c7786102837bb3d3a4c784c1191b Mon Sep 17 00:00:00 2001 From: sarah <129242944+sarah-inkeep@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:23:00 -0700 Subject: [PATCH] Fix mobile nav scroll lock left stranded on resize to desktop (#2270) * fix(docs): release mobile nav scroll lock on resize The marketing site mobile nav locked body scroll on open and only released it when the menu closed via a state change. Widening the window past the md breakpoint hid the menu via CSS without closing it, so the scroll lock was stranded and the desktop page became unscrollable. Rebuild the menu on the shadcn Sheet (Radix Dialog), which owns the focus trap, Escape handling, and body scroll lock and releases all three on close. Add a matchMedia listener that closes the sheet when crossing into desktop width. Present it as a bottom drawer with an icon and label on every row and a content-aligned close button. * fix(docs): contain overscroll on mobile nav drawer Add overscroll-contain to the Sheet drawer's scroll container so touch overscroll can't chain to the locked body when the nav exceeds the viewport. GitOrigin-RevId: 84403240c4b3c0e71565cb6e889c47e5e8e9d110 --- docs/src/app/(home)/site-nav.tsx | 200 ++++++++++++++++--------------- docs/src/components/ui/sheet.tsx | 134 +++++++++++++++++++++ 2 files changed, 240 insertions(+), 94 deletions(-) create mode 100644 docs/src/components/ui/sheet.tsx diff --git a/docs/src/app/(home)/site-nav.tsx b/docs/src/app/(home)/site-nav.tsx index 57cc809c..b173bc77 100644 --- a/docs/src/app/(home)/site-nav.tsx +++ b/docs/src/app/(home)/site-nav.tsx @@ -1,13 +1,14 @@ 'use client'; -import { Menu, Star, X } from 'lucide-react'; +import { BookOpen, Menu, Star, X } from 'lucide-react'; import Link from 'next/link'; import type { FC, SVGProps } from 'react'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import { DiscordIcon } from '@/components/icons/discord'; import { GitHubIcon } from '@/components/icons/github'; import { XIcon } from '@/components/icons/x'; import { OkWordmark } from '@/components/ok-wordmark'; +import { Sheet, SheetClose, SheetContent, SheetTitle, SheetTrigger } from '@/components/ui/sheet'; import { DOWNLOAD_ROUTE } from '@/lib/site'; import { MarketingButton } from './marketing-button'; @@ -17,15 +18,22 @@ type NavLink = { external: boolean; icon?: FC>; iconOnly?: boolean; + desktopIconHidden?: boolean; showStars?: boolean; }; -const docsLink: NavLink = { href: '/docs', label: 'Docs', external: false }; +const docsLink: NavLink = { + href: '/docs', + label: 'Docs', + external: false, + icon: BookOpen, + desktopIconHidden: true, +}; const socialLinks: NavLink[] = [ { href: 'https://x.com/OpenKnowledgeAI', - label: 'X', + label: 'X (Twitter)', external: true, icon: XIcon, iconOnly: true, @@ -49,8 +57,6 @@ const githubLink: NavLink = { const mobileLinks: NavLink[] = [docsLink, ...socialLinks, githubLink]; -const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])'; - const starFormatter = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1, @@ -58,31 +64,44 @@ const starFormatter = new Intl.NumberFormat('en-US', { const fullStarFormatter = new Intl.NumberFormat('en-US'); -function NavLinkContent({ link }: { link: NavLink }) { +function NavLinkContent({ link, surface }: { link: NavLink; surface: 'desktop' | 'mobile' }) { const Icon = link.icon; + const showIcon = !!Icon && (surface === 'mobile' || !link.desktopIconHidden); + const showLabel = surface === 'mobile' || !link.iconOnly; return ( <> - {Icon ?