@AGENTS.md — read first for Git workflow (branch + PR only, co-author trailer, secrets).
Personal website of Kai Chen — Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, deployed on Vercel.
Canonical documentation: README.md (setup, routes, APIs, env, CI, forking). Oxford portfolio assets: portfolio/oxford-dul-2025/README.md.
| Concern | Implementation |
|---|---|
| Now playing | GET /api/spotify/now-playing — Spotify Web API; optional Supabase writes (listening_*) with service role |
| Last month top | GET /api/spotify/last-month-top — Spotify top tracks (short_term, needs user-top-read) |
| Client polling | app/hooks/use-now-playing.ts — polls every 10s (cache: "no-store") |
| GitHub | GET /api/github/contributions — GraphQL calendar endpoint; hand-maintained course/personal lists merge descriptions, topics, stars, and archive state from GitHub |
| Weather | app/lib/weather.ts fetches Open-Meteo for home SSR; GET /api/weather mirrors it; berkeley-time.tsx renders local time |
| Home UI | Identity + Email/GitHub/LinkedIn/X links; Listening / server-rendered Location cards; PageToc (Kai T. Chen, Greeting, Contact, Listening, Location) |
| Music for Life | /music-for-life — curated Spotify playlists (MUSIC_FOR_LIFE_BASE); [id] detail; /map Mapbox viewer; old /playlists → 301 redirect |
| Page TOC | page-toc.tsx + toc-section.tsx — fixed right rail on /, /about, /projects, /misc, /music-for-life (not map); auto .mag-label + explicit TocSection |
| Course Projects | app/lib/course-projects.ts — Kai Course Notes entry backed by GitHub metadata; Oxford DUL remains at /projects/oxford-dul-2025 |
| Oxford portfolio | app/lib/oxford-dul-projects.ts + app/projects/oxford-dul-2025/ + app/components/oxford-dul/ |
| Nav | Desktop/mobile text links use the shared blue/yellow palette with no decorative underline (.nav-link in globals.css) |
| List link pattern | hover-link-hint.tsx — leading ↗ on hover, trailing Label ↗; row hover bg on list rows |
| Misc | Things I Love (nested mag-card-inset categories) / Resources / Watching / Remembrance |
| Typography | Geist Sans / Mono is the base system; Nunito is used across the editorial UI (nav, headings, cards, lists, controls) |
| Theme | White/black page bases; structural blue #628ea7; hover yellow #f8b364; layered blue card borders/shadows |
| Theme mode | theme-provider.tsx — default light when unset |
npm install # also sets core.hooksPath → .githooks (co-author trailer)
npm run dev # next dev (Turbopack)
npm run lint && npm run typecheck && npm run test && npm run buildapp/lib/oxford-dul-projects.ts— 6 Oxford projects + meta + disclaimerapp/lib/course-projects.ts— Course Projects card dataapp/components/oxford-dul/— portfolio UI componentsapp/components/pinned-project-link.tsx— GitHub pinned repo cards/rowsapp/components/hover-link-hint.tsx— ↗ hover hintsapp/components/page-toc.tsx— right-side page TOC rail + panelapp/components/toc-section.tsx— explicit TOC scroll targetslib/spotify-playlists.ts—MUSIC_FOR_LIFE_BASE, playlist types, map URL helperapp/music-for-life/— Music for Life index,[id],/mappublic/portfolio/oxford-dul-2025/— exported training PNGslib/now-playing.ts— Spotify types shared by API + hookapp/globals.css—.mag-card,.mag-card-inset,.nav-link, social link hovers
- Dark mode — support
dark:for new UI. - Academic integrity — Oxford pages show disclaimer; do not commit course notebooks or starter code.
- Secrets — never commit
.env.localor tokens; see AGENTS.md and README. - Pending asset —
stable-diffusionhero/results PNGs export from Colab; see portfolio README.