Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Gregory-Gerard/r2

Repository files navigation

R2

R2

Romane & Rémy, a private wedding gallery.


A static site that tells the story of one day through chapters of photos. Built as a small monorepo: a Vite app, a shared design system, and a photo pipeline that publishes to S3.

Quick start

vp install              # install workspace deps
vp run @r2/website#dev  # run the website
vp run ready            # check, test, build everything

Heads up: this repo uses Vite+ (vp). Don't reach for pnpm / npm directly. Lint, format, bundle and test all flow through the VoidZero Rust toolchain: Oxlint + Oxfmt for code quality, Vite+Rolldown for bundling, Vitest for tests.

Monorepo structure

Path What lives there
apps/website The public Vite app. Pages compose @r2/ui primitives, organized by feature.
packages/ui Design system. Tailwind v4 tokens, shadcn primitives, compound components.
tools/photos CLI that syncs .photos/ to S3 and publishes a manifest. See its README.

Photos pipeline

Drop originals into .photos/<chapter>/<name>-<order>.jpg. vp run @r2/photos#sync hashes each file (the hash is the photo's id), generates AVIF + WebP at 4 sizes plus a blurhash via sharp, uploads variants to immutable keys on S3, and writes a single manifest.json. A local cache skips unchanged files.

On the app side, a custom Vite plugin (apps/website/plugins/r2-photos.ts) fetches the manifest once when Vite starts (dev or build), strips the URLs (encoding 1MB+ of strings we can rebuild from id + size + format would be wasteful), and exposes everything as a virtual module virtual:r2-photos. The app reads photos synchronously, with zero runtime fetching of metadata.

.photos/<chapter>/<name>-<order>.jpg
        │
        ▼  vp run @r2/photos#sync
S3: photos/<id>/w{400,800,1600,2400}.{avif,webp}
S3: manifest.json
        │
        ▼  Vite plugin (dev + build)
virtual:r2-photos  →  photos, photoUrl(id, fmt, size), buildSrcSet(id, fmt)

Full walkthrough (cache invalidation, identity, source layout): tools/photos/README.md.

CI/CD

Two GitHub Actions workflows:

  • ci.yml runs vp run ready (check + test + build) on every push and pull request.
  • deploy.yml triggers on a published GitHub release (or manual dispatch), rebuilds, uploads the apps/website/dist artifact, then rsyncs it to the production host. Concurrency-guarded so two releases can't race.

To ship: publish a release on GitHub. To preview without releasing: run the Deploy workflow manually.

Scripts

vp run ready                      # check + test + build everything
vp run -r build                   # build all workspaces
vp run @r2/website#preview        # preview the production build
vp run @r2/photos#sync --dry-run  # see what would sync, without uploading

Built with Vite+, pnpm, React 19, Tailwind v4 and sharp.

About

Private wedding gallery for my brother and sister-in-law, and an excuse to play with Vite+ and the VoidZero Rust toolchain.

Resources

Stars

Watchers

Forks

Contributors