Skip to content

Update site navigation#982

Open
tannerlinsley wants to merge 3 commits into
mainfrom
taren/nav-mega-menu
Open

Update site navigation#982
tannerlinsley wants to merge 3 commits into
mainfrom
taren/nav-mega-menu

Conversation

@tannerlinsley

@tannerlinsley tannerlinsley commented Jun 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Replaces the left-nav structure with a top mega-menu hierarchy for Libraries, Learn, Community, Tools, Merch, and Support.
  • Groups library links by stack category, links category labels to their stack pages, and keeps Libraries as a direct link to /libraries.
  • Simplifies the mobile menu with collapsible groups, subtle section borders, and fixed positioning.
  • Adds a pre-hydration desktop hover fallback, glass menu styling, site blur/tint on hover, and keeps the active dropdown hover area scoped to the card.
  • Removes the library version suffix from the navbar title area and adjusts workshop full-width sections for the simplified layout.

Validation

  • pnpm test
  • In-app browser DOM checks for top-level nav labels, Support/About merge, Libraries/category hrefs, and dropdown pointer-event scoping.

Note: pnpm test passes with existing lint warnings in shop/admin utility files unrelated to this navigation change.

Summary by CodeRabbit

  • New Features
    • Redesigned navigation with a responsive mega-menu (desktop glass overlay + mobile drawer) for easier library and resource browsing.
    • Improved menu interactions: opens on hover/focus, closes on Escape/outside click, and stays in sync with route changes.
  • Style
    • Added animated underline and pane transitions for the mega-menu, including reduced-motion support.
  • Bug Fixes
    • Navbar titles no longer change based on the selected version, keeping them consistent across versioned routes.
  • Chores
    • Updated workshop section layout sizing for full-width display.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85147b77-4275-41ac-b79b-ebb3601681fc

📥 Commits

Reviewing files that changed from the base of the PR and between bb8a16d and e5dea91.

📒 Files selected for processing (1)
  • src/components/Navbar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Navbar.tsx

📝 Walkthrough

Walkthrough

Replaces the previous flyout/mobile-menu navbar with a typed, data-driven mega menu system. New internal components handle animated pane transitions, desktop triggers, mobile collapsible groups, and library browsing. CSS keyframe animations and panel visibility rules are added for all directions with reduced-motion support. Library navbar titles are simplified to remove version props, and workshop layout overrides are updated to remove sidebar width offsets.

Changes

Mega Menu Navigation Overhaul

Layer / File(s) Summary
Navbar icon imports and menu types, constants, data
src/components/Navbar.tsx
Updates lucide-react icon imports for mega-menu UI, defines menu key types, pane phases/directions, layout measurements, NAV_GROUPS configuration, and helper functions for deriving library group data from librariesByGroup.
Mega menu CSS animations, panels, and overlay styles
src/styles/app.css
Adds all .ts-mega-* selectors, custom layout properties, pane data-state/data-direction transitions, six @keyframes animations for right/left/down enter/exit, glass overlay visuals with dark-mode overrides, and prefers-reduced-motion behavior.
Collapsible trigger enhanced button props
src/components/Collapsible.tsx
Expands CollapsibleTriggerProps to accept full React.ButtonHTMLAttributes, forwarding type, onClick, onMouseDown, and other button props with event propagation stop and user handler invocation.
Navbar state management, refs, and lifecycle effects
src/components/Navbar.tsx
Adds activeMenuKey state, direction/layout/panel/container refs, delayed-close timer ref, openMegaMenu handler with direction derivation, navbar height CSS variable sync, route-change cleanup via useLocation, and global Escape/pointerdown dismissal.
Navbar header, desktop nav, mobile drawer, overlays
src/components/Navbar.tsx
Reworks header with simplified LogoSection, desktop primary nav list mapped from NAV_GROUPS with pointer/focus handlers, mobile open/close button, desktop/mobile menu overlays, site tint/blur layers, and AiDockMount.
DesktopNavTrigger, DesktopNavFallback, MobileMenuGroup
src/components/Navbar.tsx
Implements desktop trigger rendering as Link or button, fallback container holding MegaMenuContent in glass panel, and mobile collapsible groups with content switching and close on navigate.
MegaMenuContentTransition animated pane management
src/components/Navbar.tsx
Tracks previous/current menu keys, constructs enter/exit/current pane records with direction, delays transition to set panes to current, and renders MegaMenuContent per pane.
MegaMenuContent group rendering and libraries switch
src/components/Navbar.tsx
Routes libraries key to LibrariesMenuContent, renders section headers and MenuItemLink rows for other groups, optionally includes MenuRail for primary groups.
LibraryMenuItem and library group category rendering
src/components/Navbar.tsx
LibraryMenuGroup renders categories linked via groupToSlug with libraries underneath; LibraryMenuItem displays library links with color, badges, trimmed names, and separate Docs links.
MenuRail abstraction and MenuItemLink rendering
src/components/Navbar.tsx
MenuRail renders as compact rail item in mobile or card with eyebrow/title/description in desktop. MenuItemLink builds consistent menu-row layout with icon/badge/description, renders external <a> or internal TanStack Link per destination type.
LibraryNavbarTitle props and markup simplification
src/routes/-library-landing-route.tsx
Removes version prop, eliminates resolvedVersion calculation and version spans, simplifies Link className to whitespace-nowrap while retaining gradient library-name display.
Library route navbar title updates
src/routes/{ai,cli,config,db,devtools,form,hotkeys,intent,pacer,query,ranger,router,start,store,table,virtual,workflow}.$version.index.tsx
Updates all library-specific NavbarTitle components to render LibraryNavbarTitle without the version prop, removing Route.useParams() calls for version extraction.
Workshop full-width section layout cleanup
src/routes/workshops.tsx
Removes md:w-[calc(100vw-250px)] and md:mx-[...] responsive overrides from four full-width sections while retaining base w-screen/negative margin behavior.

Sequence Diagram

sequenceDiagram
  participant User
  participant DesktopNavTrigger
  participant Navbar
  participant MegaMenuContentTransition
  participant MegaMenuContent

  User->>DesktopNavTrigger: pointerenter / focus
  DesktopNavTrigger->>Navbar: onOpen(menuKey, direction)
  Navbar->>Navbar: cancelCloseTimer, setActiveMenuKey, recalcLayout
  Navbar->>MegaMenuContentTransition: activeMenuKey, direction, layout
  MegaMenuContentTransition->>MegaMenuContent: render pane (data-state=enter)
  MegaMenuContent->>MegaMenuContent: switch libraries vs general group

  User->>DesktopNavTrigger: pointerleave
  DesktopNavTrigger->>Navbar: scheduleClose(delay)
  Navbar->>Navbar: closeTimer fires → setActiveMenuKey(null)
  MegaMenuContentTransition->>MegaMenuContent: pane (data-state=exit) → hidden

  User->>Navbar: Escape keydown / outside pointerdown
  Navbar->>Navbar: clearTimer, setActiveMenuKey(null), closeMobileDrawer
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • TanStack/tanstack.com#970: This PR removes the version prop from LibraryNavbarTitle that was introduced in the related PR, updating all 17 library route components to use the simplified title component signature.

Poem

🐇 Hop, hop — the menu's new!
Mega panes slide left and right,
Glass overlays glimmer at night,
Libraries grouped with a bunny's care,
No sidebar offset anywhere.
The navbar blooms — c'est magnifique!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "Update site navigation" accurately reflects the main change: a comprehensive restructuring of the site's navigation system from a left-nav to a top mega-menu hierarchy, as confirmed by the extensive updates to Navbar component, CSS styling, and library-specific navbar titles.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch taren/nav-mega-menu

Comment @coderabbitai help to get the list of available commands and usage tips.

@tannerlinsley tannerlinsley marked this pull request as ready for review June 14, 2026 09:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/routes/-library-landing-route.tsx (1)

116-121: ⚡ Quick win

Remove the stale required version prop from LibraryNavbarTitle props.

Line 120 still requires version: string, but this component no longer reads or renders version data. Keeping it required preserves an unnecessary contract and forces avoidable prop plumbing.

Proposed diff
 export function LibraryNavbarTitle({
   libraryId,
 }: {
   libraryId: LandingLibraryId
-  version: string
 }) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/-library-landing-route.tsx` around lines 116 - 121, The
LibraryNavbarTitle function component has a stale version property in its props
type definition that is no longer used by the component. Remove the version:
string line from the props type destructuring in the LibraryNavbarTitle function
signature, keeping only the libraryId property which is actually being used.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Navbar.tsx`:
- Around line 1022-1032: When activeKey becomes null in the useLayoutEffect
hook, the panes remain mounted and focusable for MEGA_MENU_PANEL_CLOSE_MS even
though they are visually hidden via CSS, creating an accessibility issue. Fix
this by either calling setPanes([]) synchronously when activeKey becomes null
(removing the setTimeout delay) or immediately mark the closing pane as
non-focusable/inert before the timeout begins. This same issue appears at two
locations in the file around lines 1022-1032 and lines 1090-1096, so apply the
same fix to both useLayoutEffect hooks that handle pane closure.
- Around line 981-1000: The MobileMenuGroup component currently wraps the entire
group header in a CollapsibleTrigger, which makes the group label a disclosure
button rather than a direct link to group.to. To fix this, restructure the
CollapsibleTrigger to separate the navigation link from the expansion
affordance. Make the group label itself a direct link using group.to, and move
the collapsible trigger functionality (the arrow icon or expand indicator) to a
separate clickable element that controls the disclosure without triggering
navigation. This way users can directly navigate to Libraries on mobile while
still having the ability to expand the menu group.
- Around line 924-945: The Link and button trigger elements in the Navbar
component are declaring aria-haspopup="menu" and aria-expanded attributes, but
the popup content is regular navigation content that doesn't follow menu widget
patterns with proper focus management. Remove both aria-haspopup="menu" and
aria-expanded attributes from the Link and button elements since the popup
doesn't implement true menu semantics and focus continues in normal document
order.

In `@src/styles/app.css`:
- Line 145: The background property value `currentColor` is using incorrect
casing according to Stylelint's value-keyword-case rule. Change `currentColor`
to `currentcolor` (all lowercase) in the background property to normalize the
keyword casing and resolve the lint violation.

---

Nitpick comments:
In `@src/routes/-library-landing-route.tsx`:
- Around line 116-121: The LibraryNavbarTitle function component has a stale
version property in its props type definition that is no longer used by the
component. Remove the version: string line from the props type destructuring in
the LibraryNavbarTitle function signature, keeping only the libraryId property
which is actually being used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72d59333-7132-498a-b1d6-723b4e2606ec

📥 Commits

Reviewing files that changed from the base of the PR and between 762e5d8 and 432bef0.

📒 Files selected for processing (4)
  • src/components/Navbar.tsx
  • src/routes/-library-landing-route.tsx
  • src/routes/workshops.tsx
  • src/styles/app.css

Comment thread src/components/Navbar.tsx
Comment thread src/components/Navbar.tsx
Comment thread src/components/Navbar.tsx
Comment on lines +1022 to +1032
React.useLayoutEffect(() => {
if (activeKey === null) {
previousActiveRef.current = null
const timeoutId = window.setTimeout(() => {
setPanes([])
}, MEGA_MENU_PANEL_CLOSE_MS)

return () => {
window.clearTimeout(timeoutId)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear or inert the closing pane immediately.

When activeKey becomes null, the previous pane stays mounted for MEGA_MENU_PANEL_CLOSE_MS with phase: 'current'. Because the panel is only hidden with opacity/pointer-events in src/styles/app.css, those links remain reachable while the menu is visually closed. Either clear panes synchronously on close or make the closing pane non-focusable before the timeout.

Also applies to: 1090-1096

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Navbar.tsx` around lines 1022 - 1032, When activeKey becomes
null in the useLayoutEffect hook, the panes remain mounted and focusable for
MEGA_MENU_PANEL_CLOSE_MS even though they are visually hidden via CSS, creating
an accessibility issue. Fix this by either calling setPanes([]) synchronously
when activeKey becomes null (removing the setTimeout delay) or immediately mark
the closing pane as non-focusable/inert before the timeout begins. This same
issue appears at two locations in the file around lines 1022-1032 and lines
1090-1096, so apply the same fix to both useLayoutEffect hooks that handle pane
closure.

Comment thread src/styles/app.css Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Collapsible.tsx`:
- Around line 94-98: The onClick handler in the Collapsible component calls
toggle() before the consumer's onClick callback, preventing consumers from
canceling the toggle via event.preventDefault(). Reorder the logic in the
onClick handler to invoke the consumer's onClick callback first, then check if
event.defaultPrevented is true before calling toggle(). This allows consumers to
prevent toggling when needed by calling preventDefault() in their onClick
handler.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 98aa73ba-3ee2-461d-95dd-e9e7cd86173c

📥 Commits

Reviewing files that changed from the base of the PR and between 432bef0 and bb8a16d.

📒 Files selected for processing (21)
  • src/components/Collapsible.tsx
  • src/components/Navbar.tsx
  • src/routes/-library-landing-route.tsx
  • src/routes/ai.$version.index.tsx
  • src/routes/cli.$version.index.tsx
  • src/routes/config.$version.index.tsx
  • src/routes/db.$version.index.tsx
  • src/routes/devtools.$version.index.tsx
  • src/routes/form.$version.index.tsx
  • src/routes/hotkeys.$version.index.tsx
  • src/routes/intent.$version.index.tsx
  • src/routes/pacer.$version.index.tsx
  • src/routes/query.$version.index.tsx
  • src/routes/ranger.$version.index.tsx
  • src/routes/router.$version.index.tsx
  • src/routes/start.$version.index.tsx
  • src/routes/store.$version.index.tsx
  • src/routes/table.$version.index.tsx
  • src/routes/virtual.$version.index.tsx
  • src/routes/workflow.$version.index.tsx
  • src/styles/app.css
💤 Files with no reviewable changes (1)
  • src/routes/-library-landing-route.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/styles/app.css
  • src/components/Navbar.tsx

Comment on lines 94 to 98
onClick={(e) => {
e.stopPropagation()
toggle()
onClick?.(e)
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Honor consumer click cancellation before toggling.

On Line 96, toggle() runs before the consumer onClick (Line 97). With the new button-props API, consumers cannot prevent toggling via event.preventDefault(), which makes the trigger hard to compose in controlled flows.

Suggested fix
       onClick={(e) => {
         e.stopPropagation()
-        toggle()
-        onClick?.(e)
+        onClick?.(e)
+        if (!e.defaultPrevented) {
+          toggle()
+        }
       }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onClick={(e) => {
e.stopPropagation()
toggle()
onClick?.(e)
}}
onClick={(e) => {
e.stopPropagation()
onClick?.(e)
if (!e.defaultPrevented) {
toggle()
}
}}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Collapsible.tsx` around lines 94 - 98, The onClick handler in
the Collapsible component calls toggle() before the consumer's onClick callback,
preventing consumers from canceling the toggle via event.preventDefault().
Reorder the logic in the onClick handler to invoke the consumer's onClick
callback first, then check if event.defaultPrevented is true before calling
toggle(). This allows consumers to prevent toggling when needed by calling
preventDefault() in their onClick handler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant