Navigation and Layout Components
Purpose and Scope
OpenWiki’s navigation and layout components provide the shared frame around repository wikis, repository chat, and the landing experience. They solve three recurring UI problems: keeping the global product identity visible, giving repository pages a compact way to move between wiki and chat modes, and preserving user comfort through theme and scroll-state behavior. The components in this page are intentionally small, but together they define the shell contract that feature pages plug into: RootLayout owns document-level setup, OpenWikiNavbar owns the fixed top bar, mobile navigation wraps wiki page links, and footer/theme utilities provide consistent cross-page controls.
Sources: app/layout.tsx, app/components/openwiki-navbar.tsx, app/components/mobile-wiki-nav.tsx, app/components/theme-provider.tsx, app/components/theme-switcher.tsx, app/components/persistent-scroll-area.tsx, app/components/openwiki-footer.tsx, app/components/openwiki-hero-logo.tsx
The design favors server-rendered structure with targeted client components where browser state is required. app/layout.tsx is a Next.js root layout that sets metadata, font variables, initial theme classes, analytics, and speed insights around all children. Components that need browser APIs, such as ThemeSwitcher, ThemeProvider, MobileWikiNav, and PersistentScrollArea, are marked as client components and keep their state isolated from the rest of the page. That split lets route pages remain mostly declarative while still supporting cookies, media queries, dialogs, and session storage.
Sources: app/layout.tsx, app/components/theme-provider.tsx, app/components/theme-switcher.tsx, app/components/mobile-wiki-nav.tsx, app/components/persistent-scroll-area.tsx
Relevant Source Files
app/layout.tsx— Defines the root HTML shell, global metadata, Geist font variable, early theme initialization script,ThemeProvider, Vercel Analytics, and Speed Insights.app/components/openwiki-navbar.tsx— Renders the fixed OpenWiki header, Vercel and OpenWiki branding, optional repository crumb, optional wiki/chat mode switcher, deploy link, GitHub repository link, and mobile wiki navigation slot.app/components/mobile-wiki-nav.tsx— Provides a client-side dialog for mobile wiki page navigation and closes the dialog when a contained link is clicked.app/components/theme-provider.tsx— Defines theThemeunion, React context, provider, anduseThemePreferencehook used by theme controls.app/components/theme-switcher.tsx— Implements the dark, light, and system theme selector, cookie persistence, DOM class updates, and system theme change handling.app/components/persistent-scroll-area.tsx— Wraps scrollable sidebars in an accessibleasidewhile saving and restoring scroll position fromsessionStorage.app/components/openwiki-footer.tsx— Renders a simple footer that exposes a compact theme switcher.app/components/openwiki-hero-logo.tsx— Provides the large typographic OpenWiki mark used by hero-style pages.
Root Layout and Global Shell
The root layout establishes the baseline environment every page inherits. It imports global CSS, configures the Geist font with the --font-sans CSS variable, and exports Next.js metadata with the product title and the description “Generate a living, source-grounded wiki for any GitHub repository. Built on eve.” In the rendered document, it sets lang="en", applies the font variable and default dark class to <html>, and uses suppressHydrationWarning because the client-side theme script can change the class before React hydrates.
Sources: app/layout.tsx
Theme initialization is handled before the body renders by an inline script with id theme-init. The script reads the openwiki-theme cookie, resolves system against prefers-color-scheme: dark, applies either dark or light to document.documentElement, writes data-openwiki-theme-preference, sets colorScheme, and applies an immediate background color. This early script is important because it prevents a flash of the wrong background while the React client code loads. If cookie parsing or media-query access fails, the script falls back to dark mode, matching the server-side default theme.
Sources: app/layout.tsx
Inside the body, ThemeProvider wraps page children and the Vercel telemetry components. The body uses shared Tailwind-style classes for minimum screen height, background, foreground text, and antialiasing, so individual pages can assume the same visual baseline. Analytics and SpeedInsights are mounted inside the provider but after the page children, which keeps product instrumentation present across the app without forcing each route to import it. The layout therefore acts as both a visual shell and a runtime provider boundary.
Sources: app/layout.tsx, app/components/theme-provider.tsx
Navigation Components
OpenWikiNavbar is the primary application header. It renders as a fixed top bar with a border, background, and high z-index, so route content should account for its h-10 height when laying out page bodies. The left side starts with an external Vercel link, a slash separator, and an internal Link to / labeled OpenWiki. When a route supplies a repo prop, the header extends into a repository breadcrumb that includes the repository icon, label, and link to the repository’s OpenWiki page.
Sources: app/components/openwiki-navbar.tsx
Repository-aware pages can also supply wikiHref, chatHref, and activeMode on the repo object. When both mode URLs are present, the navbar renders a desktop-only navigation group labeled “Repository mode” with Wiki and Chat links. The active state is derived from activeMode: wiki is active unless the mode is explicitly chat, and chat is active when activeMode === "chat". This keeps route ownership simple; pages only provide canonical links and current mode while the navbar enforces consistent presentation.
Sources: app/components/openwiki-navbar.tsx
The right side of the navbar carries operational and source links. It includes a “Deploy OpenWiki with Vercel” external link using DEPLOY_WITH_VERCEL_URL, a conditional mobile wiki navigation slot, and an external GitHub link based on OPENWIKI_REPOSITORY_URL and OPENWIKI_REPOSITORY_FULL_NAME. The showRepositoryLinkLabelOnMobile prop changes the GitHub link from an icon-sized control into a truncated labeled control on smaller screens. This allows pages with different density needs to choose whether the repository label remains visible on mobile.
Sources: app/components/openwiki-navbar.tsx
Mobile Wiki Navigation and Persistent Sidebars
MobileWikiNav adapts a wiki page list into a slide-in dialog for small screens. It owns an open state, uses a DialogTrigger button with the accessible label “Open wiki pages,” and renders a left-aligned dialog constrained to min(88vw,360px). The dialog title is “Wiki pages,” with a screen-reader-only description explaining that the content navigates between pages for the repository. The component accepts arbitrary children, so the same page tree used elsewhere can be inserted without duplicating navigation markup.
Sources: app/components/mobile-wiki-nav.tsx
A small interaction detail makes the mobile drawer feel route-aware: the component listens for clicks in the scrollable content region and closes the dialog when the event target is inside an anchor. That means any wiki page link placed inside the drawer automatically dismisses the navigation after selection. The component does not inspect route data or modify the link; it simply treats anchor activation as completion of the mobile navigation task. This keeps the mobile wrapper generic and reusable across repository wiki pages.
Sources: app/components/mobile-wiki-nav.tsx
PersistentScrollArea supports desktop-style navigation and outline panels that should remember their scroll position as users move between routes. It renders an accessible aside with a required ariaLabel, optional class name, and caller-provided storageKey. On scroll, it writes the current scrollTop to window.sessionStorage. On pathname or storage-key changes, it reads the saved value, validates that it is finite, applies it immediately, and applies it again on the next animation frame to survive layout shifts during route transitions.
Sources: app/components/persistent-scroll-area.tsx
Theme System and Footer
The theme system has two layers: a context for React state and DOM/cookie logic for browser persistence. ThemeProvider defines the public Theme type as "dark" | "light" | "system", stores the current value in React state, and exposes it through useThemePreference. The hook deliberately throws useThemePreference must be used inside ThemeProvider. when called outside the provider, which makes integration errors fail loudly during development instead of silently rendering an unbound control.
Sources: app/components/theme-provider.tsx
ThemeSwitcher is the visible control. It presents a role="group" labeled “Theme” with three options: Dark with Moon, Light with Sun, and System with Monitor. Each option is a button with an accessible label such as “Use dark theme,” an aria-pressed selected state, and a data-openwiki-theme-option value. The compact prop switches button sizing and hides text labels, which is why the footer can expose the same behavior in a narrow visual footprint.
Sources: app/components/theme-switcher.tsx, app/components/openwiki-footer.tsx
On mount, ThemeSwitcher reads openwiki-theme, applies the stored preference, updates context state, and registers a prefers-color-scheme: dark listener. Theme selection writes a one-year cookie with Path=/ and SameSite=Lax, updates the root dark or light class, sets data-openwiki-theme-preference to the chosen preference, updates colorScheme, and sets a matching background color. OpenWikiFooter composes this functionality by rendering a centered footer with a compact switcher, giving users a persistent place to recover or change their preference.
Sources: app/components/theme-switcher.tsx, app/components/openwiki-footer.tsx
Component Reference
| Component | Public props or contract | Key behavior |
|---|---|---|
RootLayout | { children: React.ReactNode } | Sets metadata, font class, initial dark theme, early theme script, ThemeProvider, analytics, and speed insights. |
OpenWikiNavbar | mobileWikiNav?, showRepositoryLinkLabelOnMobile?, repo? with activeMode, chatHref, href, iconSrc, label, wikiHref | Renders product navigation, optional repository crumb, desktop wiki/chat mode switcher, deploy link, mobile nav slot, and GitHub link. |
MobileWikiNav | { children: ReactNode } | Opens wiki navigation in a mobile dialog and closes when a nested anchor is clicked. |
ThemeProvider | { children: ReactNode; initialTheme: Theme } | Provides theme and setTheme through context. |
useThemePreference | No arguments | Returns the theme context or throws if no provider is present. |
ThemeSwitcher | { compact?: boolean } | Lets users select dark, light, or system theme and persists the preference in openwiki-theme. |
PersistentScrollArea | { ariaLabel: string; children: ReactNode; className?: string; storageKey: string } | Renders an aside that saves and restores scroll position per storage key. |
OpenWikiFooter | No props | Renders a footer containing a compact theme switcher. |
OpenWikiHeroLogo | No props | Renders the large OpenWiki wordmark for hero sections. |
Implementation Guidance
When adding a new repository page, prefer composing the existing shell instead of creating route-specific navigation. Pass repository identity into OpenWikiNavbar, provide both wiki and chat URLs when a mode switch is appropriate, and use MobileWikiNav only as the mobile wrapper around the same wiki page list used elsewhere. For scrollable sidebars, choose a stable storageKey that matches the panel’s purpose rather than the current route, because PersistentScrollArea already restores on pathname changes. For theme controls, always render them beneath ThemeProvider, which is already true for normal application routes.
Sources: app/components/openwiki-navbar.tsx, app/components/mobile-wiki-nav.tsx, app/components/persistent-scroll-area.tsx, app/components/theme-provider.tsx, app/components/theme-switcher.tsx
A good next step is to read the wiki routing and repository home component pages alongside this page. Routing pages show where the navbar’s repository props and mobile wiki content originate, while repository home components show how product-level navigation connects to indexing, wiki rendering, and chat entry points. If you are changing branding, start with OpenWikiNavbar and OpenWikiHeroLogo; if you are changing global visual behavior, start with app/layout.tsx, ThemeProvider, and ThemeSwitcher.