Site architecture
Big picture: one edge service, profile data baked into the deploy, what loads in the browser, and how tabs and exports fit together.
Overall designs
Tab Designs
Site Feature Designs
How the pieces fit together
This page summarises how the live site is structured end to end—the same story as the project’s technical DESIGN.md, told for visitors. For each screen, use the tab and feature links in the navigation.
What I optimised for
The site is deliberately small and self-contained: one program at the edge (Cloudflare) serves every request.
Career facts live in structured source data, not in a separate CMS. Months, industry totals, and skill totals are calculated on the server so charts, downloads, and the API always agree.
There is no sign-in editor and no client-side router: the career views are in-page tabs only, so refreshing the page always starts on Industries again.
- One deployable unit instead of a separate frontend build pipeline for the main app.
- A printable / downloadable HTML profile built from the same numbers as the interactive view.
- Out of scope today: multi-tenant editing, logged-in authoring, or deep-link URLs for each tab.
Where it runs
The browser talks to that edge service over HTTPS. There is no database or key–value store in the current design: the profile is compiled into the deployment package, and the worker serves a precomputed profile JSON object from memory.
Optional features (for example chat or board-game feeds) use secrets on the server only; they are not embedded in the page source.
What loads when the career page opens
First comes a small HTML document with the page frame and global styles. Then a single JavaScript file runs. It fetches one JSON document that describes the whole public profile.
Only after that data arrives do the hero line, donuts, timeline, lists, and other widgets render. If the fetch fails, an error message appears instead of half-populated charts.
How raw data becomes the on-screen view
Authors maintain roles with start and end months, industry references, skills, capabilities, and impact bullets. A server-side build step turns that into the shape the browser consumes: each industry and skill gets totals and fractions; each role gets a resolved duration, including “present” roles.
Capabilities are normalised with stable slugs so several roles can share one donut slice. Month arithmetic is shared logic so the API, downloads, and UI stay in sync.
Important URLs (simplified)
- Home page — HTML shell and embedded styles.
- /api/profile — public JSON used by the interactive résumé.
- /assets/app.js — main browser logic.
- /assets/journey.js, /assets/ribbon.js, /assets/constellation.js — extra scripts loaded the first time those tabs open.
- /api/boardgames/recent — optional recent-play feed (cached briefly at the edge; supports a limit query for grid sizing).
- /api/chat — optional Ask endpoint (same-origin checks and rate limits applied).
- /assets/org-logos/… — small logo images embedded at build time when available.
- /download/profile (and related path) — self-contained HTML file for saving or printing.
- /design/… — these “how it works” pages.
Tabs and accessibility
The tab strip uses standard tab semantics: one panel is visible at a time, others are hidden. Keyboard focus shows a clear outline.
Visually the tabs are plain text spaced evenly across the content column, not pill buttons inside a heavy frame.
Heavy visuals load on demand
Journey, Ribbon, and Constellation each ship as their own script bundle. Nothing downloads until that tab is first selected, which keeps the first load lighter.
Ribbon may use WebGL; if reduced motion, data saver, or WebGL is unavailable, a static diagram is shown instead.
Generative backdrop
After profile data loads, the page derives a stable colour recipe from the sorted list of role identifiers and feeds it into CSS custom properties.
Gradients are static in CSS (no extra canvas, no drift animation). Reduced motion at the OS level still applies to other UI transitions.
The one-page download is print-oriented and does not include this backdrop layer.
Consistent panel height on large screens
On wide viewports the main tab areas share a common minimum height based on the tallest rendered tab panel, measured off-screen at the current content width. Side panels and long lists scroll inside their own region so the overall column does not grow and shrink wildly when switching tabs.
On narrow screens that rule is turned off so stacked layouts can scroll naturally.
Security and privacy (short version)
- Profile text is treated as data: anything interpolated into HTML is escaped on the server and in sensitive client paths.
- The profile API is public JSON—assume anything in it could be copied.
- There is no account system or session store in this codebase; chat uses CV text kept on the server only, not pasted into the page.
- HTML/API/script responses include baseline security headers; chat requests are same-origin checked and rate-limited.
Deploys and caching
Shipping a new version of the worker picks up profile changes bundled with it. Cache policy is route-specific: HTML and APIs stay short-lived, app.js is immutable and versioned, and board-game API responses are cached briefly at the edge.
Design tradeoffs worth noting
- The main script is one large inline bundle string in source form—simple operationally, heavier to edit than a separate SPA toolchain.
- Date helpers exist in two places (server TypeScript and a small ES5 embed for the browser) so behaviour stays aligned when either side changes.
- Open-ended roles use an internal “present” month for maths only; the UI still says Present where appropriate.
Simple layout sketch
Request flow: the browser, the edge service, and the assets or data it returns.