Jinawong Jino

Colophon

How this site is built

Small site, team-sized setup. The point is the process: anyone on a team — engineer or not — can ship a change without breaking production.

One monorepo

pnpm workspaces and Turborepo, the same shape I use at work. The site and the component library live side by side; Turborepo runs lint, typecheck, test and build across both and caches what didn't change.

jinawong-site/
├─ apps/site/                 Astro site (static output)
│  ├─ src/content/            case studies (Markdown + zod schema)
│  ├─ src/pages/              routes — /, /work, /about, /ui …
│  ├─ functions/api/          Cloudflare Pages Function (contact, off)
│  └─ wrangler.toml           Cloudflare Pages project config
├─ packages/ui/               React + TypeScript component library
│  ├─ src/tokens/             design tokens (CSS variables, light/dark)
│  ├─ src/components/         Button, Card, Tag, Timeline, …
│  └─ .storybook/             Storybook
├─ .claude/commands/          Claude Code slash commands
├─ .github/workflows/ci.yml   lint → typecheck → test → build → Lighthouse → preview
├─ CLAUDE.md                  conventions for humans and agents
└─ turbo.json                 task graph + caching

Astro, with islands only where needed

Every page is pre-rendered to static HTML. React components from the library render on the server and ship zero JavaScript by default. Only the component gallery hydrates a React island, and only when it scrolls into view (client:visible). The theme toggle in the header is the library component's static HTML, wired by a few lines of framework-free script — no React on the page.

A component library with design tokens

packages/ui is a small React + TypeScript library: Button, Card, Tag, Timeline, MetricStat, SectionHeading and ThemeToggle. Styles are SCSS Modules that read only from design tokens — CSS variables for color, spacing and type, with light and dark themes. Each component has Storybook stories and Vitest + Testing Library tests that check behavior and accessibility (roles, names, aria-pressed), not snapshots.

Content with a schema

Case studies are Markdown files in an Astro Content Collection. A zod schema defines every field. A missing field, a wrong date format or a typo in a key fails astro check in CI, with a message that says which file and which field.

period: z.string().regex(/^(Mon )?YYYY( – (Mon YYYY|Present))?$/),
stack:   z.array(z.string()).min(2).max(10),
problem: z.string().min(40).max(500),
outcome: z.array(z.string()).min(1).max(5),
// z.strictObject: an unknown or misspelled key fails the build

CI with budgets

GitHub Actions runs the whole pipeline on every pull request. Lighthouse CI enforces a performance and accessibility budget of 95 — a slow or inaccessible change fails like a failing test.

pull request
   │
   ├─▶ lint         ESLint (TS, Astro, React hooks, jsx-a11y)
   ├─▶ typecheck    tsc + astro check  ◀── content schema runs here
   ├─▶ test         Vitest + Testing Library (packages/ui)
   ├─▶ build        astro build + storybook build
   ├─▶ lighthouse   perf ≥ 95 · a11y ≥ 95, or the PR fails
   └─▶ preview      Cloudflare Pages preview URL on the PR

Cloudflare Pages

Static output deploys to Cloudflare Pages, with a preview URL for every pull request. A tiny Pages Function for a contact form is included behind Cloudflare Turnstile and stays switched off until it's configured.

Non-engineers ship safely, with Claude Code

The repo is set up so that someone who doesn't write code can still add a case study and get it live, with guardrails at every step:

  1. Open the repo in Claude Code and run /new-case-study. It asks a few plain-language questions and creates the file from a template.
  2. Claude follows CLAUDE.md — the house rules: where content goes, the schema, the tone, and what never to publish (private screenshots, internal code, secrets).
  3. It runs the same checks as CI locally (pnpm check) and fixes what fails before opening a pull request.
  4. CI repeats every check, and Cloudflare posts a preview link to review the real page.
  5. An engineer approves and merges. Production updates on merge.

The written guide is CONTRIBUTING-for-non-engineers.md in the repo. Built with Claude Code, directed and reviewed by me — the same way I work day to day.