Source for reetikpatel.me — my personal site.
- Nuxt 4 · Vue 3 · TypeScript
- Tailwind CSS v4 via
@tailwindcss/vite - @nuxt/fonts — Fraunces (display) + Manrope (sans), self-hosted
- @nuxt/icon — Iconify (
simple-icons+lucide), pre-bundled for first paint - Three.js + custom GLSL for the landing paint-stroke shader
- Deployed to Cloudflare Workers via Wrangler (Nitro
cloudflare_modulepreset)
app/
├── app.vue # root — favicon links, SEO meta, page-nav overlay
├── pages/
│ ├── index.vue # landing — seigaiha + paint strokes
│ ├── about.vue
│ ├── journey.vue
│ └── projects.vue
├── components/
│ ├── SeigaihaWaves.vue # tiled 青海波 SVG pattern
│ ├── SeigaihaLoader.vue # animated variant used during nav
│ ├── LandingPaintStrokesGL.vue # Three.js + GLSL paint-stroke ribbons
│ ├── Navbar.vue
│ ├── PageShell.vue # shared interior-page wrapper (bg + H1 + footer)
│ └── SiteLoading.vue / SiteFooter.vue
├── composables/ # useSiteReady, usePageNavTransition
├── config/site.ts # nav links, social URLs, contact info
└── assets/css/main.css # design tokens + .iris-wipe + .external-link
public/ # favicons, og-image, static assets
cohost/ # second deployments of the sites /projects embeds
├── deploy.sh # clone → build → deploy each preview Worker
└── seed-steven-wise/ # one-off D1 + R2 seeder for the Steven Wise copy
wrangler.jsonc # Cloudflare Worker config (custom domain: reetikpatel.me)
nuxt.config.ts
bun install
bun run dev # http://localhost:3000bun run build # nuxt build → .output/
bun run preview # build + wrangler dev (runs on the Workers runtime locally)Use bun run build, not bun build — the latter is Bun's own bundler
subcommand and will not run the Nuxt script.
bun run deploy # build + wrangler deploy → reetikpatel.meRequires wrangler login and access to the Cloudflare account bound in
wrangler.jsonc.
/projects renders some projects as live miniatures
inside an iframe (MiniSitePreview.vue)
rather than as screenshots. Those iframes deliberately do not point at the
projects' production hostnames — most of those sites are client-owned, and
embedding production would spend a client's bandwidth and break the preview
whenever they change their site.
Instead each embeddable project is deployed a second time into my own
Cloudflare account, on a subdomain of this site — the same convention the other
side projects already follow (job, now-playing, arkanpute, cl,
hackathon). projects.vue builds those URLs with its cohost() helper.
Each site keeps its own wrangler.jsonc pointed at its own production domain;
cohost/deploy.sh generates a wrangler.cohost.jsonc
override at deploy time that repins account_id and swaps routes for a
reetikpatel.me subdomain, so a co-host deploy can neither land in a client's
account nor bind a client's hostname.
./cohost/deploy.sh # all of them
./cohost/deploy.sh steven-wise # a subset| Project | Host | Worker | Notes |
|---|---|---|---|
| NGG Dandiya | nggdandiya.reetikpatel.me |
ngg-dandiya-preview |
React Router 8 SSR, public site only + its own D1 and R2, sales off |
| Steven Wise | stevenwise.reetikpatel.me |
steven-wise-preview |
Nuxt 4 SSR + its own D1 and R2 |
| All AV Services | allav.reetikpatel.me |
allavservices-preview |
Next.js static export, assets-only Worker |
| Pura Coco | puracoco.reetikpatel.me |
official-puracoco |
Nuxt 4 SSR — deployed from its own repo, not by deploy.sh |
Pura Coco is not in deploy.sh: it was already co-hosted on this domain before
any of this existed, so it needs no second copy.
Clones land in the gitignored cohost/.work/, so the first run is slow. All AV
Services also needs a media pass — its export ships unoptimised source
photography well past the 25 MiB Workers asset limit, so deploy.sh re-encodes
anything over 8 MiB in the build output before uploading.
Steven Wise needs data as well as code: its gallery reads artwork metadata from
D1 and image bytes from R2. The copy gets its own steven-wise-portfolio-preview
database and steven-wise-works-preview bucket, seeded once by
cohost/seed-steven-wise — a throwaway Worker that
copies objects at the edge, because the R2 REST API returns object bodies as
lossily decoded strings and corrupts every image. Deploy it, walk /seed?offset=
until /verify reports no missing objects, then delete it.
That copy's ACCESS_AUD is intentionally not a real Cloudflare Access
audience, so /admin and /api/admin return 403 on the preview. The gallery is
public and unaffected. Pointing it at the real audience would stand up a second,
unguarded admin console over a client's data.
NGG Dandiya is three Workers — public site, organizers' admin, ticketing API —
and only the public site is co-hosted. The copy has no service binding to the
API, which the site's tickets page already reads as "checkout not configured",
and its ngg-db-preview database is rebuilt from the repo on every deploy
(migrations, the seed, then sales_enabled = 0), so it lists prices with sales
closed and never holds an order or a buyer. The site builds through
@cloudflare/vite-plugin, whose generated deploy config targets the client's
account and domain, so deploy.sh carries only its bundle shape into the
co-host config. SITE_URL stays https://nggdandiya.com: the copy serves
noindex and points its canonicals at the real site. The sponsor logos in
ngg-sponsor-logos-preview are a one-time copy of the public /sponsors/*
files; copy them again if the real lineup changes.
- Seigaiha (青海波) is the visual anchor — landing background, page-nav
loader, and the entire favicon / OG image set are all the same wave motif
rendered from
SeigaihaWaves.vue. - Palette lives as CSS custom properties in
app/assets/css/main.css:--ink#151514,--cream#ceb9a1,--wave-ink#7a8a82, with a warm gold accent (#8C6A1F/#C9A24A) for the seigaiha stroke. - Landing scroll is locked via
useHeadclasses so the shader canvas stays pinned; interior pages scroll normally.