sahin.io

All skills and agents

site-chrome-navigation

The chrome around every page on sahin.io/angel-investments — static header and mega menu, React NavBar, SiteSidebar, MobileSectionNav, footer, breadcrumbs — and the YAML that drives them (navigation.yaml, chrome.yaml, page-crumbs.yaml). Use when building, rebuilding or changing the menu, a menu page's label/title/description, the sidebar, the mobile tabs, the footer or breadcrumbs.

Source file
.claude/skills/site-chrome-navigation/SKILL.md

Site chrome and navigation

What it is

Two copies of the same chrome, fed by the same YAML:

  • Static (server/publicChrome.ts): written into every pre-rendered document by renderDocument() in server/seoRouteManifest.ts — renderSiteHeader() (brand + <details class="site-mega-menu"> built from PUBLIC_NAVIGATION_SECTIONS), renderBreadcrumbNav() (<nav class="crumb">), renderSiteFooter() (Category and Sub-Category chips from facts.derived, first FOOTER_SUBCATEGORY_PREVIEW = 18 sub-categories then a show-all toggle, social links, editorial links). siteNavigationLinks() adds About and the deck CTA (the /about and /pitch routes when they exist, else the person URL and a mailto).
  • React (client/src/components/): NavBar.tsx (header, ThemeSwitcher, GlobalSearch, and it mounts SiteSidebar.tsx + MobileSectionNav.tsx), Footer.tsx (SPA hub only), shared helpers in siteNavigation.ts (NAV_SECTIONS, MENU_COPY, SECTION_ICONS by section id, EXTRA_LINKS, isCurrent, sectionForPath, usePagePath).

On a React page, server/publicReactDocument.ts drops the static header, keeps the static breadcrumb and footer HTML and hands them to PublicReactPageView as ServerChrome (data-server-crumb, data-server-footer divs), so crumbs and footer are identical on both paths. App.tsx pads content by var(--app-sidebar-width); the sidebar collapsed state is in localStorage key sidebar-collapsed (SIDEBAR_STORAGE_KEY).

Files

File Role
content/shared/navigation.yaml hub (Portfolio entry), sections (id, label, ordered pages paths; / is the hub), pages (every menu page: path, label, title, description), compactLinks
content/shared/chrome.yaml brand, menu, sidebar, breadcrumb, head, footer, search copy; placeholders {name}, {handle}, {section}, {query}, ...
content/pages/page-crumbs.yaml breadcrumb names of React SINGLE_PAGES outside the menu, by path (pageCrumbCopy() in server/staticPageCopy.ts)
client/src/generated/copy/shared/navigation.ts, chrome.ts generated by node scripts/sync-site-copy-module.mjs; never edit by hand
shared/publicNavigation.ts LEGACY_MENU_PAGES, PUBLIC_NAVIGATION_SECTIONS, PUBLIC_NAVIGATION_ITEMS, EXPLORE_PATH
server/publicChrome.ts static header/menu/crumb/footer, PUBLIC_PAGE_CSS, headSpec, renderHead
client/src/lib/themeVars.ts tv() / tvMix() colours used by all React chrome (see theme-system)

How it works

  • Sections: explore, insights, stories, research, specs (ids are stable keys for icons and element ids; change labels, never ids). Each menu page belongs to exactly one section.
  • A section may add links (label + path, e.g. /skills-and-agents#sna-agents anchors) after its pages; they render in every menu but are not routes. specs holds /skills-and-agents plus anchors to its Foundation, Pages, Quality & Ship and Agents groups; sectionForPath() also matches detail pages under an item's path (/skills-and-agents/{name} → Specs).
  • Every pages entry becomes a legacyFeature route in createSeoRouteManifest() (LEGACY_MENU_PAGES → route(page.routePath, "legacyFeature", page.title)), unless a reviewed editorial singleton owns the path (e.g. /thesis). Its title is the <title>; its description is the meta description for paths in MENU_DESCRIPTION_FEATURES, else the recovery-surface intro (RECOVERY_SURFACE_BY_PATH, content/pages/recovery-surfaces.yaml).
  • Breadcrumbs: hub crumb (hubCrumb()) then the page. React shell pages use the spec's crumb (SINGLE_PAGES from page-crumbs.yaml; FAMILIES derive it from their title). Company crumbs add the primary Category. The JSON-LD BreadcrumbList uses the same list (see seo-head-and-schema).
  • scripts/generate-static.ts fails the build if any page lacks an href from NAVIGATION_HREFS (every section link), so the menu stays crawlable on React pages.

How to change

  • Add a menu page: add path/label/title/description under pages and the path under one section in navigation.yaml; run node scripts/sync-site-copy-module.mjs; build the page (new-react-page, or a static legacyFeature body); give it a URL per public-url-conventions.
  • Rename/reorder: edit labels or order in YAML only. Code matches pages by path, never by label.
  • Add a non-menu React page crumb: add /path: Name to page-crumbs.yaml (its SinglePage.path type is keyof ReturnType<typeof pageCrumbCopy>, so a missing crumb is a type error).
  • Footer/header wording: chrome.yaml only (page-content-yaml).

Rebuild from scratch

  1. Write navigation.yaml, chrome.yaml, page-crumbs.yaml; run the copy sync.
  2. shared/publicNavigation.ts maps YAML into sections/items with /angel-investments hrefs.
  3. Static chrome in server/publicChrome.ts; renderDocument() composes header, <main class="shell">, crumb, body, footer.
  4. React chrome: NavBar + SiteSidebar + MobileSectionNav using siteNavigation.ts and tv() colours; App.tsx layout.
  5. PublicReactPageView renders server crumb/footer HTML around React pages.
  6. deploy-gate → ui-before-after (desktop and mobile; sidebar and mobile tabs) → ship-to-production.

Tests

server/shared-content.test.ts (menu, sections, placeholders; static header/menu/footer/crumb copy), server/seo-route-manifest.test.ts, server/public-react-document.test.ts (server crumb/footer inside React pages), server/public-schema.test.ts (BreadcrumbList), server/content-in-yaml.test.ts.