Skip to main content
The YWCC Industry Capstone site is a Jamstack + Selective SSR monorepo. Public-facing content is pre-rendered at build time into static HTML and served from Cloudflare’s CDN. Authenticated portal pages are server-rendered on demand by Cloudflare Workers. Content flows from Sanity Studio through GROQ queries into Astro at build time, with no runtime CMS calls on production.

System diagram

Layered architecture

Key architectural decisions

DecisionChoiceRationale
Rendering strategyStatic-first with per-route SSRLighthouse 95+ for public pages; dynamic auth for portal
CMSSanity (headless)Visual Editing, structured content, real-time API
DeploymentCloudflare Pages + WorkersEdge computing, D1 database, Durable Objects
AuthBetter AuthMulti-provider (OAuth + Magic Link), D1 adapter
DatabaseCloudflare D1 (SQLite)Serverless, zero-config, edge-local
Rate limitingCloudflare Durable ObjectsPer-IP state, SQLite-backed sliding window
UI frameworkAstro + React islandsMinimal client JS; hydration only where needed
StylingTailwind CSS v4CSS-first config, design tokens, multi-theme
Component libraryshadcn / fulldev-ui patternCopy-paste ownership, 39+ primitive families

Public pages (SSG) vs portal pages (SSR)

SSG — 8 public pages

Pre-rendered at build time. Served directly from Cloudflare’s CDN. No auth required.
  • / — Home (index.astro)
  • /sponsors/ — Sponsor listing
  • /sponsors/[slug] — Sponsor detail
  • /projects/ — Project listing
  • /projects/[slug] — Project detail
  • /events/ — Events listing
  • /events/[slug] — Event detail
  • /[...slug] — CMS-driven pages

SSR — 9 portal/auth pages

Server-rendered per request by a Cloudflare Worker. Auth middleware runs on every hit.
  • /portal/ — Sponsor dashboard
  • /portal/[sponsorSlug] — Sponsor-specific view
  • /portal/events — Portal events
  • /portal/progress — Project progress
  • /portal/login — Login screen
  • /portal/denied — Access denied
  • /student/ — Student dashboard
  • /auth/login — Auth entry
  • /api/auth/* — Better Auth handler (5 endpoints)

Cloudflare edge services

ServiceBindingPurpose
Cloudflare PagesHosts static HTML/CSS/JS from the CDN
Cloudflare WorkersBundled with PagesRuns SSR pages and API endpoints at the edge
D1 (SQLite)PORTAL_DBStores user accounts, sessions, OAuth accounts, magic-link verification tokens
KVSESSION_CACHECaches validated sessions for 5 minutes to avoid D1 round trips
Durable Objectsrate-limiter-workerPer-IP sliding window: 100 requests / 60 s; alarm-based cleanup
The output: 'static' setting in astro.config.mjs is the project default. Pages that need SSR opt in individually via export const prerender = false. The @astrojs/cloudflare adapter bridges both modes under a single Cloudflare Pages project.

Performance targets

MetricTarget
Lighthouse Performance95+
Lighthouse Accessibility90+
Cumulative Layout Shift< 0.05
JS payload< 5 KB minified
CSS payload< 15 KB after Tailwind purge
No framework runtime ships to the browser — Total Blocking Time stays near zero because Astro outputs plain HTML by default and only hydrates React islands in the portal.

Build docs developers (and LLMs) love