Skip to content

Redesign documentation navigation to horizontal top nav bar#391

Open
WGB5445 wants to merge 2 commits into
mainfrom
feat/top-navigation-bar
Open

Redesign documentation navigation to horizontal top nav bar#391
WGB5445 wants to merge 2 commits into
mainfrom
feat/top-navigation-bar

Conversation

@WGB5445

@WGB5445 WGB5445 commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Redesign: Move Top-Level Navigation to Horizontal Top Nav Bar

Redesign the documentation navigation to follow the pattern used by Claude Code Docs and other modern documentation sites (e.g. Stripe, Vercel).

Problem

The current sidebar uses vertical tabs for top-level categories (Guides, SDKs & Tools, Smart Contracts, etc.), which takes up significant vertical space and requires users to click a tab before seeing sub-navigation. This pattern is less discoverable and harder to scan than a horizontal top navigation bar.

Solution

Move top-level categories to a horizontal navigation bar positioned below the main header, and simplify the left sidebar to show only the sub-navigation for the currently active section.

Files Changed

File Change
src/components/TopNav.astro New — Horizontal nav component: reads sidebar data, extracts top-level groups, renders as links with active underline indicator
src/starlight-overrides/Header.astro Integrated TopNav as second row; renamed inner div to avoid class conflict with PageFrame
src/starlight-overrides/Sidebar.astro Major simplification — Removed TabbedContent tab switcher (−193 lines), now renders only the active section's entries with a section title
src/starlight-overrides/PageFrame.astro Desktop header uses flex-direction: column for two-row layout; sidebar/content offset via --sl-header-total-height
src/styles/global.css Added --sl-top-nav-height and --sl-header-total-height CSS custom properties

Responsive Behavior

  • Desktop (≥50rem): TopNav shown in header bar; sidebar shows sub-navigation only
  • Mobile (<50rem): TopNav hidden from header; shown inside the sidebar drawer so mobile users can still switch sections

Verification

  • pnpm check — 0 errors, 0 warnings, 0 hints (192 files)
  • pnpm lint — biome + prettier both pass
  • pnpm build — Full production build succeeds (1150 HTML pages)
  • pnpm test — 14/14 tests pass
  • Link validation✓ All internal links are valid (built-in starlight-links-validator)
  • No URL breakage — No routes were added/removed/renamed; all existing URLs work as before

What This Does NOT Change

  • No URL changes — All page URLs remain identical
  • No content changes — Documentation text/translations are untouched
  • No sidebar config changesastro.sidebar.ts is not modified; sidebar data structure is the same
  • No dependency changes — No packages added or removed

…av bar

Redesigned the documentation navigation to follow the pattern used by
Claude Code Docs and other modern documentation sites:

Before: Top-level categories (Guides, SDKs, Smart Contracts, etc.) were
rendered as vertical tabs inside the left sidebar.

After: Top-level categories are now a horizontal navigation bar below
the main header, with the left sidebar showing only the sub-navigation
for the currently active section.

Changes:
- New TopNav.astro component: horizontal nav bar with active state
  indicator (underline accent), extracted from sidebar group data
- Header.astro: integrated TopNav below the main header row
- Sidebar.astro: removed TabbedContent tab switcher, now shows only
  the active section's entries with a section title header
- PageFrame.astro: adjusted sidebar content padding for cleaner layout

Responsive behavior:
- Desktop (>=50rem): TopNav shown in header, hidden from sidebar
- Mobile (<50rem): TopNav hidden from header, shown inside the
  sidebar drawer for mobile menu access
@vercel

vercel Bot commented Mar 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aptos-docs Ready Ready Preview, Comment Mar 9, 2026 4:30am

Request Review

The TopNav was being squeezed into the same row as the logo/search
because PageFrame's header had a fixed height constraint.

Fix:
- Added --sl-top-nav-height and --sl-header-total-height CSS variables
- PageFrame header now uses flex-direction: column on desktop to stack
  the header content and TopNav vertically
- Sidebar and main-frame positioning use --sl-header-total-height on
  desktop so they offset correctly below both header rows
- Header.astro inner content takes --sl-nav-height height, TopNav fills
  the remaining --sl-top-nav-height space
@WGB5445 WGB5445 marked this pull request as ready for review March 9, 2026 12:04
Copilot AI review requested due to automatic review settings March 9, 2026 12:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Redesigns the documentation navigation UI to use a horizontal top navigation bar for top-level sections, while the left sidebar shows only the active section’s sub-navigation (with responsive behavior for mobile vs desktop).

Changes:

  • Added a new TopNav.astro component to render top-level documentation sections horizontally.
  • Updated Starlight overrides (Header.astro, Sidebar.astro, PageFrame.astro) to integrate the top nav and simplify the sidebar to only the active section.
  • Introduced a desktop-only CSS variable for combined header height to support correct sticky sidebar/layout offsets.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/styles/global.css Adds desktop-only CSS variables for top-nav/header combined height.
src/starlight-overrides/Sidebar.astro Replaces tabbed sidebar UI with active-section-only sidebar and mobile TopNav placement.
src/starlight-overrides/PageFrame.astro Adjusts header/sidebar sizing and sticky offsets to account for the new top nav row.
src/starlight-overrides/Header.astro Renders TopNav as a second row in the header on desktop viewports.
src/components/TopNav.astro New component to render top-level groups as a horizontal navigation bar.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +55
return {
label: group.label,
icon: sidebarGroupIcons[key] || "ph:rocket-launch",
href: findFirstLink(group.entries) || "#",
isActive: hasCurrentPage(group.entries),

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

The returned topGroups objects include an icon field, but the icon is never rendered in the markup. This makes the sidebarGroupIcons/label-to-key mapping (and the icon computation) unnecessary work/coupling. Either render the icon in the nav items or remove the icon/key/labels mapping until it’s needed.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +66
topGroups.map(({ label, href, isActive }) => (
<a class:list={["top-nav-item", { active: isActive }]} href={href}>
{label}
</a>

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

Expose the active state to assistive tech by setting aria-current on the active top-level link when isActive is true. This helps screen readers announce the current documentation section.

Copilot uses AI. Check for mistakes.
Comment on lines 5 to 6
import { stripLeadingAndTrailingSlashes } from "node_modules/@astrojs/starlight/utils/path";
import { sidebarGroupIcons } from "../../astro.sidebar.ts";
import labels from "../../src/content/nav/en";
import TabbedContent from "~/components/tabs/TabbedContent.astro";
import TabListItem from "~/components/tabs/TabListItem.astro";
import TabPanel from "~/components/tabs/TabPanel.astro";
import { stripLangFromSlug } from "~/utils/path-utils";

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

stripLeadingAndTrailingSlashes is being imported via the bare specifier node_modules/@astrojs/starlight/utils/path, which is not a stable/resolvable module path in standard Vite/Astro resolution (it will try to resolve a package named node_modules). Import from Starlight’s public entrypoint (e.g. @astrojs/starlight/utils/path) or inline a small local helper to avoid a build-time module resolution failure.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +76
const activeGroup = sidebarGroups.find(
(group) => group.type === "group" && group.entries && hasCurrentPage(group.entries),
);
const activeEntries = activeGroup?.entries ?? sidebarGroups[0]?.entries ?? [];
const activeSectionLabel = activeGroup?.label ?? "";

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

activeEntries falls back to the first group’s entries when no group matches the current page, but activeSectionLabel does not fall back similarly. This can render a blank section title while still showing entries (e.g., if isCurrent isn’t set for a route). Consider falling back to sidebarGroups[0]?.label when activeGroup is undefined so the title always matches the displayed entries.

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +70
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const sidebarGroups = (Array.isArray(sidebarData) ? sidebarData : []) as any as SidebarEntryType[];

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

There are two consecutive eslint-disable-next-line @typescript-eslint/no-explicit-any comments above the same cast. One of them is redundant; please remove the extra disable to keep the lint annotations minimal.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +37
function hasCurrentPage(entries: SidebarEntry[]): boolean {
return entries.some((entry) => {
if (entry.type === "link") return !!entry.isCurrent;
if (entry.type === "group" && entry.entries) return hasCurrentPage(entry.entries);
return false;

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

TopNav determines the active section via entry.isCurrent, but this component doesn’t apply the custom “mark current entries” logic that Sidebar.astro applies (including the Move Reference subpath handling / isSubPage behavior). Because the header renders before the sidebar in PageFrame, desktop TopNav can compute isActive before Sidebar mutates Astro.locals.starlightRoute.sidebar, leading to missing/incorrect active highlighting. Consider moving the current-marking logic into a shared utility and invoking it from TopNav too (or deriving active group from route info without relying on sidebar mutation).

Copilot uses AI. Check for mistakes.
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.

3 participants