ascii-cafe

ascii.cafe

Coming soon page. Turn anything into living ASCII art.

Next.js App Router, TypeScript, no CSS framework. The imagery is a single <canvas> per stage driven by the vendored glyph-raster engine, so there are no image assets and nothing to load over the network.

Run it

yarn install
yarn dev

Yarn 4, nodeLinker: node-modules. There is deliberately no package-lock.json: two lockfiles let the local tree and the deploy build drift apart. If port 3000 is busy, Next picks the next free one and prints it.

It runs with an empty environment. See below for what that means for the signup form.

The signup form

The form posts to /api/subscribe, which resolves a provider at request time from the environment and never exposes a key to the browser.

Provider When it is used
kit KIT_API_KEY is set
sendy the three SENDY_* vars are set
console nothing is set

console is the fallback so a clean checkout runs. It logs each address with a loud warning and then discards it. The UI still shows success, because the alternative is a form that looks broken in development. Do not launch that way.

To go live, copy .env.example to .env.local and set KIT_API_KEY.

Moving from Kit to Sendy later

The site talks to MailingProvider in src/lib/mailing/types.ts and nothing else, so the swap is one env var. What the code cannot do for you:

src/lib/mailing/sendy.ts is written but has never run against a live install. Test it against a throwaway list first.

Deploying

Vercel. It detects Next.js and yarn with no config file.

Set KIT_API_KEY in the Vercel project’s environment variables, not in the repo. Attach the domain in Vercel’s dashboard: the CNAME file in this repo is a GitHub Pages artifact and is inert on Vercel, kept only so the Pages path stays open.

One caveat about the throttle

src/app/api/subscribe/route.ts holds its counters in module memory. On Vercel that memory is per lambda instance, so under real concurrency the global guard becomes “100 per minute per instance”, which can collectively overshoot Kit’s account-wide 120 per minute. It still does its main job of stopping a single hammering client, and a coming soon form is unlikely to reach that ceiling. If it ever does, move both counters to Vercel KV or Upstash; the function signatures are already shaped for it.

Layout

src/
  app/
    api/subscribe/route.ts   validation, per IP throttle, provider dispatch
    globals.css              the whole design, hand written
    layout.tsx, page.tsx
  components/
    Hero.tsx                 full bleed cup, pointer and ripple interaction
    MenuTiles.tsx            the four sources
    SignupForm.tsx
  lib/
    glyph-raster.js          vendored engine, UMD, no dependencies
    glyph-raster.d.ts        hand written types for the parts used here
    cup-field.ts             the raymarched cup, a pure (u, v, t) field
    density.ts               cell size to column count
    mailing/                 provider seam: kit, sendy, console

Notes