Production-Grade UI Refactor — Design Spec

Date: 2026-04-14 Status: Approved for implementation planning Target: system-design-ultimatum static docs site generated by build-site.js Visual reference: Vercel / Nextra documentation sites — clean, airy, minimal color, strong hierarchy, great code blocks.

Goal

Refactor the site's UI to a production-grade standard: extract CSS/JS from the monolithic build-site.js into real source files, refine the visual language, rework layout and navigation, add standard modern-docs interactions, and meet accessibility/performance expectations — all while preserving every existing feature.

Non-Goals

  • No framework migration. The site remains vanilla HTML generated by Node.
  • No bundler, PostCSS, Tailwind, or new runtime dependencies.
  • No content changes.
  • No rewrite of the Excalidraw diagram viewers.
  • No change to the search backend (still client-side over search-index.json).

Features Preserved

Every current feature must continue to work after the refactor:

  • Sidebar navigation with collapsible folders
  • Client-side search over search-index.json
  • Right-rail table of contents
  • Persisted dark-mode toggle
  • Prev/next page navigation
  • Excalidraw diagram viewers
  • Mobile drawer navigation
  • Dashboard/homepage with category grid
  • Breadcrumbs, back-to-top button, reading progress bar

Section 1 — Architecture & File Layout

Extract all styles and client-side JavaScript from the globalStyles template literal and inline <script> blocks in build-site.js into dedicated source files.

assets/
  site.css              ← all styles (new)
  site.js               ← all client-side JS (new)
  fonts/
    Geist-*.woff2       ← self-hosted
    GeistMono-*.woff2
build-site.js           ← pure HTML generator
docs/assets/            ← generated output (copied at build)
  site.<hash>.css
  site.<hash>.js
  fonts/…

Build flow:

  1. build-site.js reads assets/site.css and assets/site.js from disk.
  2. Computes a short sha1 content hash per file.
  3. Writes to docs/assets/site.<hash>.css / .js.
  4. Copies fonts to docs/assets/fonts/.
  5. Emits <link rel="stylesheet" href="assets/site.<hash>.css"> and <script defer src="assets/site.<hash>.js"></script> in the page template.
  6. The existing globalStyles constant and inline scripts are deleted from build-site.js.

Why hashed filenames: long-term caching without manual cache-busting, no runtime cost, no tooling required.

Inline exception: a tiny (~10 line) theme-init script stays inline in <head> to prevent FOUC — it reads localStorage.theme and sets data-theme on <html> before first paint.


Section 2 — Visual Language

Typography

  • Sans: Geist (self-hosted woff2). Fallback chain: Geist, Inter, -apple-system, BlinkMacSystemFont, sans-serif.
  • Mono: Geist Mono. Fallback: 'Geist Mono', 'JetBrains Mono', ui-monospace, monospace.
  • Body: 15px / 1.7.
  • Scale: h1 2rem, h2 1.5rem, h3 1.2rem, h4 1rem.
  • Negative letter-spacing on headings (-0.02em).
  • Generous top margins on h2/h3 for section separation.
  • Max content width: 720px (down from 860).

Color tokens

Light theme:

  • --bg: #ffffff
  • --bg-subtle: #fafafa
  • --bg-muted: #f4f4f5
  • --text: #0a0a0a
  • --text-secondary: #52525b
  • --text-muted: #a1a1aa
  • --border: #e4e4e7
  • --border-strong: #d4d4d8
  • --accent: #0070f3
  • --accent-bg: rgba(0, 112, 243, 0.08)

Dark theme:

  • --bg: #0a0a0a
  • --bg-subtle: #111111
  • --bg-muted: #18181b
  • --text: #fafafa
  • --text-secondary: #a1a1aa
  • --text-muted: #71717a
  • --border: #27272a
  • --border-strong: #3f3f46
  • --accent: #3b82f6
  • --accent-bg: rgba(59, 130, 246, 0.12)

One accent color only. No purple/teal/amber.

Surfaces & depth

  • Flat by default. Borders over shadows.
  • One shadow token --shadow-lg for elevated surfaces (Cmd-K modal, search dropdown).
  • Radii: 6px default, 10px cards, 4px inline code.

Motion

  • 150ms ease for hover/focus.
  • 200ms for theme attribute transition.
  • No transform: translateY on hover (drop card-lift).
  • Focus ring: 2px accent, 2px offset, visible in both themes.
  • @media (prefers-reduced-motion: reduce): disable all transitions and smooth scroll.

Code blocks

  • Dark background in both themes (#0a0a0a).
  • Subtle 1px border.
  • Top-right copy button, top-left language label (from first line or lang class).
  • Line-height 1.65, 14px.
  • Inline code: --bg-muted background, --text color (not accent), 4px radius.

Callouts (info / warning / success)

  • Left border 3px in the tone color.
  • Tinted background derived from that tone at ~8% alpha.
  • Same typography as body.

Section 3 — Layout & Navigation

Top bar (new)

  • Sticky, 56px tall, full-width, z-index above content.
  • backdrop-filter: blur(8px) with translucent bg so content shows through.
  • Border-bottom --border.
  • Contents: brand link (left) · Cmd-K search trigger (center-left) · spacer · theme toggle · GitHub link (right).
  • The search trigger is a button that shows "Search…" + ⌘K kbd hint and opens the palette on click.

Left sidebar

  • 280px wide, starts flush under the top bar, independent scroll.
  • No header inside — just nav content.
  • Folders rendered as <details> with persisted open/closed state in localStorage under key nav:<folder-slug>.
  • The folder containing the current page always force-opens on load.
  • Active page link: 2px accent left-border + --accent-bg, not a pill.
  • Section labels: uppercase 11px, letter-spacing: 0.08em, --text-muted.

Right TOC

  • 240px wide, sticky below top bar.
  • Shown at viewport ≥ 1280px. Hidden below.
  • Same visual treatment as the sidebar (1px left-border on the nav, active item gets accent color + medium weight).
  • Scroll-spy: IntersectionObserver on h2/h3 inside .content-body, rootMargin: "-80px 0px -70% 0px". Active heading's TOC link gets .is-active.
  • Click smooth-scrolls to target (unless prefers-reduced-motion).

Content column

  • Centered, max-width: 720px.
  • Breadcrumb row above h1 (smaller, --text-muted).
  • Headings h2/h3/h4 with an id get a hover-visible <a class="heading-anchor" href="#id">#</a> appended. Clicking copies the full URL and updates location.hash.
  • Prev/next at bottom inside a bordered two-card row.

Mobile (< 768px)

  • Top bar stays visible; left hamburger replaces brand area actions.
  • Sidebar becomes a drawer that slides in from the left, with a backdrop overlay.
  • TOC hidden.
  • Content padding reduces to 1rem.
  • Cmd-K palette still works; the top-bar search trigger opens it full-screen.

Removed / replaced

  • Search input inside sidebar → replaced by top-bar trigger + Cmd-K modal.
  • Theme toggle inside sidebar → moved to top bar.
  • Floating .kbd-hint pill bottom-left → removed; shortcuts documented in Cmd-K footer.
  • Reading progress bar kept, but thinner (2px) and solid accent.

Section 4 — Interactions & Client JS

All vanilla JS in assets/site.js. No dependencies. Modules below; each is a small IIFE or function called from a single init() on DOMContentLoaded.

1. Theme

  • Inline <head> script reads localStorage.theme (or matchMedia('(prefers-color-scheme: dark)')) and sets data-theme on <html> before paint.
  • Top-bar toggle button updates storage and the attribute. Dispatches a themechange custom event for any listeners.

2. Command palette

  • Overlay modal, 640px wide, centered, fades in 150ms.
  • Triggers: ⌘K, Ctrl+K, / (when not focused in an input), click on top-bar search trigger.
  • Dismiss: Esc, backdrop click.
  • Input fetches search-index.json (cached after first load). Fuzzy-match over title + path + snippet fields. Highlights matched substring with <mark>.
  • Result rows: title, folder breadcrumb, snippet.
  • Keyboard: / navigate, open, ⌘↵ / Ctrl+↵ open in new tab.
  • Empty state: recent pages (last 5 from localStorage.recent).
  • Footer row shows keybinds.
  • role="dialog", aria-modal="true", focus trap, restores focus on close.

3. TOC scroll-spy

  • IntersectionObserver over all h2, h3 inside .content-body that have ids.
  • rootMargin: "-80px 0px -70% 0px".
  • Add .is-active to the matching TOC link; remove from others.

4. Sidebar folder persistence

  • On <details> toggle event, write localStorage["nav:<slug>"] = "open" | "closed".
  • On load, restore each folder's state; always force-open the folder containing the current page.

5. Copy-code buttons

  • For each <pre> in .content-body, wrap in a relative container and inject a Copy button top-right.
  • Click copies pre.textContent via navigator.clipboard.writeText, swaps label to Copied for 1.5s.

6. Heading anchor links

  • For each h2/h3/h4 with an id, append a # link.
  • CSS shows it on hover of the heading.
  • Click copies full URL to clipboard and updates history.replaceState hash.

7. Reading progress bar

  • Scroll listener (rAF-throttled). Computes window.scrollY / (document.documentElement.scrollHeight - window.innerHeight). Sets --progress CSS variable on the bar element.

8. Back-to-top

  • Shows after 400px scroll via class toggle.
  • Click smooth-scrolls to top (respects reduced-motion).

9. Mobile drawer

  • Hamburger button toggles nav-open class on <html>.
  • Backdrop click / Esc closes.
  • Focus trap within the drawer while open.

10. Keyboard shortcuts

  • ⌘K / Ctrl+K / / → open palette
  • t → toggle theme (only when no input focused)
  • g h → navigate to home
  • Esc → close palette or drawer

Section 5 — Accessibility, Performance, Build

Accessibility

  • Semantic landmarks: <header>, <nav aria-label="Primary">, <main>, <aside aria-label="On this page">, <footer>.
  • All interactive controls are real <button> or <a>; focus ring always visible.
  • Skip-to-content link, visible on focus.
  • Cmd-K modal: role="dialog" + aria-modal="true" + focus trap + focus restore on close.
  • WCAG AA color contrast in both themes — token values above are chosen to meet this for body and secondary text.
  • @media (prefers-reduced-motion: reduce): disable transitions and smooth scroll.

Performance

  • Self-hosted Geist + Geist Mono woff2 with font-display: swap, preloaded via <link rel="preload" as="font" crossorigin>. Drops the Google Fonts CDN request.
  • CSS and JS shipped as content-hashed static files for long-term caching.
  • site.js loaded with defer.
  • Inline <head> theme-init script is tiny (~10 lines) to prevent FOUC.
  • Images keep loading="lazy".

Build changes to build-site.js

  • New loadAssets() helper: reads assets/site.css and assets/site.js, computes crypto.createHash('sha1').update(contents).digest('hex').slice(0, 8) per file, writes to docs/assets/site.<hash>.css and .js, returns { cssHref, jsHref }.
  • New copyFonts() helper: copies assets/fonts/* to docs/assets/fonts/.
  • The globalStyles template literal is deleted.
  • Inline <script> blocks embedded in page templates are deleted (moved into site.js).
  • Layout helpers take cssHref / jsHref and emit <link> and <script defer> tags.
  • No new npm dependencies.

Out of scope (explicitly not changed)

  • Markdown → HTML conversion logic
  • Excalidraw diagram rendering
  • search-index.json generation
  • Folder tree / navigation generation
  • Dashboard / category card data

Acceptance Criteria

  1. Every feature listed in "Features Preserved" continues to work.
  2. build-site.js no longer contains the globalStyles template literal or any inline client-side <script> blocks (except the small theme-init FOUC-prevention script).
  3. assets/site.css and assets/site.js exist as editable source files.
  4. Generated pages link to docs/assets/site.<hash>.css and .js with a content-hashed filename.
  5. Cmd-K / Ctrl-K / / opens a command palette that searches search-index.json.
  6. TOC right-rail highlights the currently visible section as the user scrolls.
  7. Sidebar folder open/closed state persists across page loads.
  8. Each <pre> has a working copy button.
  9. Each h2/h3/h4 with an id has a hover-visible anchor link.
  10. Theme toggle lives in the top bar, persists, and does not FOUC on reload.
  11. No regression in mobile layout; sidebar drawer works.
  12. WCAG AA contrast met in both themes for primary and secondary text.
  13. prefers-reduced-motion disables transitions and smooth scroll.
  14. No new runtime dependencies introduced.
System Design Ultimatum · Last updated 4/28/2026