Selected work
Full-stack WebReact 19 · Vite · Node/Express · MySQL · Docker Compose

NeatPour

Swipe cocktails Tinder-style, train your flavor fingerprint, hold up a glowing bartender card with QR — all without signing up.

A zero-signup cocktail discovery app that learns your taste preferences through swipes, lets you rank favorites head-to-head via ELO, and generates QR-coded bartender cards you can hold up at the bar. Signed-in owners get photo uploads, recipe editing, consumption tracking, and granular sharing modes — guests get read-only access over a shared password.

Role — Solo developer
  • React 19
  • Vite 7
  • Tailwind CSS v4
  • Motion (Framer)
  • Node.js
  • Express 5
  • MySQL 8
  • Docker Compose
  • Caddy
  • Sharp
  • QRCode

NeatPour runs on three core concepts. First, the "Taste Lab" — a flavor-fingerprint engine that trains on every swipe without requiring an account. The engine lives in client/src/lib/taste.js and scores drinks by their tags and category, ranking unrated drinks by how well they match your palate. Swipe data is persisted in localStorage, no backend writes needed. Second, owner login unlocks full control: add/edit/delete drinks, upload photos (auto-resized to WebP by Sharp), log consumption, and manage shares. Third, a password-gated guest mode lets you share the entire app read-only to friends — the API enforces this via a `role` claim in the JWT, and guest sessions cannot mutate anything.

The front-end is React 19 + Vite 7 + Tailwind v4, with Motion for Tinder-style card swipes and spring animations. Design is "Midnight Art-Deco Speakeasy": noir-green-black canvas with brass-gold and absinthe-mint accents, film-grain overlay, and neon-sign glows. Icons are Phosphor via react-icons, aliased through a single file so the set can be swapped. The Tinder deck supports both drag-swipes and keyboard shortcuts (→ like, ← dislike, Z/Backspace undo). At large screens (≥1024px), the layout switches from mobile's bottom tab bar to a persistent left sidebar, and content becomes multi-column.

On the backend, Express 5 runs the API with JWT auth in httpOnly cookies, Zod schemas for validation, and multer + sharp for photo handling. MySQL 8 holds users, drinks (with ELO rank_score), consumption logs, share links, and guest votes. Docker Compose orchestrates three services: a Node server on :3000, MySQL on :3306 (named volume for persistence), and Caddy as a reverse proxy + SPA host on :80/:443. Caddy routes /api to the server, serves /uploads as read-only files, and handles both raw-IP (self-signed) and domain (Let's Encrypt) TLS modes. First run shows a one-time owner setup screen; the 47-drink starter list seeds automatically.

What's notable
  • Flavor-fingerprint profiling: anonymous visitors get personalized drink rankings based on their swipes alone, with no signup required.
  • Three access modes: owner login (full control), guest password (read-only over the entire app, enforced server-side), and public share links (specific collections or single drinks, with optional guest voting).
  • Tinder-style swipe deck with keyboard shortcuts, ELO-based head-to-head ranking system, and consumption tracking with 30-day bar chart and category breakdowns.
  • QR-coded bartender cards: generate a cream-colored menu plaque with QR that resolves to the public recipe page — designed to pop on a dark phone in a dark bar.
  • Art-Deco design system: custom Tailwind palette (brass, absinthe, neon coral), Abril Fatface + Jost typefaces, Motion animations gated behind prefers-reduced-motion, and 1% film-grain overlay.