Vite · Supabase · Claude · Git · Vercel

Architecture
for designers

How to build your own stack, stop depending on a screen generator — and turn localhost into an edge case hunter.

Wellington Mota — Product Engineering

Act I — The stack Act II — Edge Case Hunter Coda — Ship

Where we start

Lovable gives you speed.
Then it gives you a ceiling.

Black box
You describe, a screen appears. But the build, the routing, the database and the deploy live on the other side of the glass.
Cost per try
Every iteration burns credits. The visual refinement loop — which is exactly your job — becomes the most expensive line item in the project.
Export, no manual
You can export the code. What comes out is a project you never configured, full of decisions nobody explained to you.
Complexity ceiling
Row-level permissions, migrations, seed data, state testing. There's a day the tool doesn't come along.

None of this is a knock on the product. It's the price of an environment that decides things for you — and the bill arrives the day you need to decide.

The trade

Vite runs the app.
Supabase holds the data.
Claude writes the code.

Three pieces you swap independently. None of them owns your project — each solves one problem and gets out of the way.

And a fourth piece, the most important and the cheapest: your browser, open on localhost.

Git keeps the history. Vercel publishes. None of these five things charges you for looking at the same screen again.

Anatomy

Six layers. No arrow crosses a black box.

Browserlocalhost:5173 — the only screen where judgment happensDevTools · Zoom · Keyboard
ViteDev server, HMR and the production buildReact · TypeScript · Tailwind
SupabasePostgres, auth, files and row-level permissionsSDK in the client · SQL in the DB
Claude CodeReads, writes and runs the project from your terminalCLAUDE.md · MCP · Screenshots
Git + GitHubThe readable history and the undo that AI doesn't havecommit · branch · diff
VercelThe public URL — a deploy on every push, a preview per branchpreview · production · rollback

Every layer has public docs, a competing alternative and an exit door. That's what separates architecture from a subscription.

The dev server

The server
you already have.

Vite serves your project's files straight to the browser, without bundling anything first. The server is up in milliseconds and you work on port 5173.

HMR swaps only the component that changed — no page reload. What you typed in the form is still there. The modal is still open. Step three of the wizard is still on step three.

Why it matters
You save the file and the screen changes before your hand leaves the keyboard. That's the loop — the same one you have in Figma, now running on real code.
In the end
npm run build outputs static HTML, CSS and JS. That's what Vercel publishes.
Exit doors
Next.js, Astro, SvelteKit — same idea, more opinions. Vite is the smallest thing that does the job, and the easiest to leave.

The data

A real Postgres,
with a door in front.

Database

Postgres. Tables, relations, SQL. You open the editor and see the data — not an abstraction of it.

Auth

Email and password, magic link, Google, GitHub. The session arrives already resolved in the client.

RLS

The rule of who sees what lives in the database, not in an if in the frontend.

Storage

Image and file uploads, under the same permission rules.

Realtime

The table changes, the screen changes. Useful for shared lists and notifications.

Local

supabase start runs the whole database in Docker on your machine. No internet needed.

Running the database locally is what closes the loop in Act II: you seed absurd data on purpose — a 74-character name, an order with no items, a user with no photo — and watch what the interface does.

The builder

Not a screen generator.
Someone who opens the files.

Reads the project
The whole repository, not just the prompt. It knows Button.tsx already exists and that your spacing scale goes by 4.
Runs the project
Executes npm run dev, reads the error in the terminal, applies the fix and runs again. The error doesn't turn into an apology — it turns into a line of code.
Looks at the screen
Opens localhost:5173 in the browser, takes a screenshot, measures the element that overflowed, resizes to 320px and does it again.
Writes the history
Makes the commit with the right message. You read the git diff before accepting.

The difference between asking for a screen and asking for a fix: the second requires the tool to see the current state. Only something with the repository and the browser can.

The undo

Git is the Cmd+Z that survives closing the laptop.

It's the piece designers postpone the most and need the most. Working with AI, Git stops being developer bureaucracy and becomes a safety net: if the last round made everything worse, one line takes you back.

# before the first screen, not after
git init
git add -A && git commit -m "scaffold"

# one experiment per branch
git switch -c feat/editorial-home
# didn't work out? the whole experiment disappears:
git switch main
git diff
Your code review. You read exactly what the AI changed, line by line, before accepting. Never commit what you haven't read.
.gitignore
.env.local and node_modules stay out. The Supabase key never goes to GitHub.
The history
In a screen generator, history is a list of prompts. Here it's a list of changes you can read, revert and explain to someone else.

Day one

From zero to localhost in six lines.

# 1. the project
npm create vite@latest my-app -- --template react-ts
cd my-app && npm install && git init

# 2. style and data
npm install @supabase/supabase-js
npm install -D tailwindcss @tailwindcss/vite

# 3. the database, on your machine
npx supabase init && npx supabase start

# 4. the loop starts here
npm run dev            → http://localhost:5173

After that, claude in the same directory. It now sees everything above.

The local Supabase key shows up in the output of supabase start. It goes into .env.local with the VITE_ prefix — without it, Vite won't expose the variable.

Six lines. None of them is irreversible, none of them charges per run, and the result is yours before it exists.

Never touched a terminal?

The whole setup is one ask away.

01 — Node.js
Download the LTS installer at nodejs.org and click through it like any app. This is the only step with no shortcut.
02 — Claude Code
Get it at claude.com — the site hands you one line to paste in the Terminal. Claude app can walk you through both installs if anything looks scary.
03 — Open it
Make a folder for the project, open the Terminal there, type claude. From here you stop typing commands and start asking. Claude Code
04 — First ask
Create a Vite project here with React, TypeScript and Tailwind, start the dev server and open it in my browser.

Every command on the previous slide is something you can type — and never have to. The six lines become one sentence.

Prompts to steal

Connect the pieces by asking.

Supabase
Set up Supabase in this project: install the client, create src/lib/supabase.ts reading the keys from .env.local, and tell me exactly where in the dashboard I find those two values.You create the free project at supabase.comClaude Code does the rest.
GitHub
Create a private GitHub repo for this project and push it. If I'm not logged in, walk me through gh auth login step by step.
Vercel
Connect this repo to Vercel, add my two Supabase keys as environment variables, and deploy. Give me the URL.
When it breaks
Read the error in the terminal, explain it in one sentence, then fix it.

Topography

Where each decision lives.

my-app/
├─ CLAUDE.md
├─ .env.local
├─ vercel.json
├─ src/
│  ├─ tokens.css
│  ├─ components/
│  ├─ routes/
│  │  └─ hunt/
│  └─ lib/supabase.ts
└─ supabase/
   └─ migrations/
CLAUDE.md
The contract: how this project behaves. Next slide.
tokens.css
Color, type, space, radius. The source of truth. No literal hex outside this file.
components/
One file, one component. If there are two, it isn't done yet.
routes/hunt/
All of Act II lives here. It doesn't ship to production.
migrations/
The database's history, versioned in Git next to the code.

The contract

The file that makes the AI
stop improvising.

You write the rules once. Every future session starts with them on the table. It's a design system written in prose — and it's the piece a designer writes better than a developer.

Without this file, every conversation starts from scratch and every component invents a new gray.

# CLAUDE.md

## Visual
- Every color comes from src/tokens.css.
  A literal hex in a component is a bug.
- Spacing on the 4px scale only.
- Display face: serif. Body: Inter.

## Components
- One file per component.
- Every new component gets a case in
  src/routes/hunt/ before it ships.

## Forms
- Label always visible. Never placeholder-only.
- Error below the field + aria-describedby.
- Submit locks on the second click.

## House rules
- Never install a UI library without asking.
- Small commits, messages in the imperative.

The method

Four steps. Only one
of them can't be automated.

01 — Describe
In plain language, with the context CLAUDE.md doesn't cover. "An order card with photo, status and amount. Follows the tokens."
02 — Generate
It writes the file, runs the server, fixes its own type and syntax errors before handing it back.
03 — Look
You, in your browser. Not an embedded preview, not a screenshot. The real screen, at the width you choose.
04 — Correct
"The amount is competing with the status. Set the amount in tabular figures and move the status below." — a designer's vocabulary, not a developer's.

Step 3 is yours, and it's the only one you can't outsource: it's judgment. The rest of the architecture exists to make that step fast and cheap.

Act I scorecard

What changes places.

Screen generatorYour own stack
Where it runsOn their serverOn your machine, on localhost
The errorA summary in natural languageThe stack trace, in your terminal
TokensApproximated inside generated CSSOne file, imported by everything
DatabaseAbstracted behind a panelPostgres, with SQL and migrations
HistoryA list of promptsGit — readable, reversible, yours
PublishingTheir button, their domainVercel: a preview per branch, your domain
Cost to iteratePer promptSaving the file
CeilingExists, and you find it lateYours

The real prize isn't any single row — it's that no company sits between you and your work. Every piece is swappable, every file lives on your machine, and if a tool disappears or doubles its price, you walk away owning everything you built.

Act II — Edge Case Hunter

The happy path is
twenty percent
of the work.

The other eighty are the situations nobody designed: the 74-character name, the network that dropped mid-submit, the empty list on first login, the server error on a field that was already correct.

You don't find these in Figma. You find them in the browser — and that's why Act I exists.

Taxonomy

Six axes. Every component is crossed by all six.

Data

Empty. One. Many. Too long. Emoji and accents. Zero. Negative. Null. Right-to-left text.

State

Idle. Loading. Saving. Success. Error. Disabled. Read-only. Partially filled.

Network

Slow. Offline. 500. Timeout. Responses arriving out of order. Session expiring midway.

Input

Paste. Browser autofill. Double-click on submit. Keyboard only. Browser back with a draft.

Environment

320px. 4K. Dark theme. Large system font. 200% zoom. Reduced motion.

Permission

Logged out. No access to that row. Viewer role. Suspended account. Someone else's data.

Six axes × eight states × three widths × two themes. Nobody clicks through that by hand. So we render all of it at once.

The tool

A route in your own app that renders everything that can go wrong.

What it is
A /hunt route inside the same project. Same CSS, same tokens, same fonts, same theme. The component there is the production component — not a copy.
Why not a screenshot
Because a screenshot has no keyboard focus, no zoom, no screen reader, no prefers-reduced-motion, and doesn't break at 320px.
Why localhost
It's the real browser. You open DevTools, drag the window down to 320px, bump the system font size, turn on VoiceOver. Everything the user can do, you do first.
Doesn't ship
One line in the router keeps the route out of the final build — or you leave it in, behind auth, and it becomes the team's QA page.

The harness

You describe the cases. The matrix builds itself.

// src/routes/hunt/text-field.cases.ts
export default defineCases(TextField, {
  base:      { label: 'Name', value: 'Ana Prado' },
  empty:     { value: '' },
  long:      { value: 'Maria das Graças A. Sant\'Anna' },
  unicode:   { value: '🇧🇷 José Ávila 🌱' },
  error:     { error: 'Enter a name' },
  errorLong: { error: 'An account with this name already…' },
  pending:   { pending: true },
  disabled:  { disabled: true },
})
// src/routes/hunt/index.tsx
<CaseMatrix
  cases={cases}
  viewports={[320, 768, 1280]}
  themes={['light', 'dark']}
/>

// 8 cases × 3 widths × 2 themes
// = 48 screens on one page

Each case is three lines. Writing the case is faster than opening the screen and reproducing the situation by hand — and it stays there forever.

What you see when you open the route

Eight versions of the same field, side by side.

TextField · viewport 320 px · light theme · zoom 100%

base
NameAna Prado
empty
NameYour full name
CLIPPED
long74 chars
NameMaria das Graças Albuquerqu
unicode
Name🇧🇷 José Ávila 🌱
error
NameAnaEnter your last name
PUSHES
errorLong2 lines
NameAna PradoAn account with this name already exists in this organization.
pending
NameChecking…
disabled
NameAna Prado

The clipping in the long case doesn't show up in a Figma export. It shows up here, at 320px, in the third cell — and the two-line error pushes the button below the fold in the sixth.

Where it hurts most

The form is the battlefield.

  1. A 74-character name. Does it wrap, truncate, or leak out of the card?
  2. josé+test@company.com.br Does your validation accept + and accents?
  3. Password pasted from a manager. Does onChange fire, or does the field stay "empty"?
  4. Browser autofill. Does the floating label move up, or sit on top of the text?
  5. Double-click on Submit. Two rows in the database?
  6. Network drops mid-submit. Does the button recover, or spin forever?
  1. Error coming from the server. Does it land next to the right field — or in a toast that vanishes?
  2. Browser back. Does the draft survive, or does the person type it all again?
  3. 200% zoom. Is the submit button still reachable?
  4. Keyboard only. Can you reach submit? Does the Tab order make sense?
  5. Submit with an empty field. Does focus jump to the first error?
  6. Screen reader. Is the error announced, or just painted red?

Twelve lines of cases. Twelve bugs that never reach the user — and none of them needed a QA person clicking.

The hunt

It opens the same screen you do.

You ask
"Open /hunt/text-field at 320px and tell me what overflows."
It navigates
Drives the browser, resizes the window, takes a screenshot, reads the DOM and measures how far the element went past its container. In pixels, not opinions.
It fixes the right thing
Fixes the component, never the test case. If the case stopped reproducing, the bug was hidden, not solved.
The matrix reloads
Vite's HMR. All 48 screens redraw. You check the other 47 with your eyes — because fixing one almost always breaks another.
It becomes a commit
git diff, you read it, you accept it. The case stays in the repository: next month, it'll still be testing.

Its job is to find. Yours is to decide whether it's good.

Coda

A component is
done when:

  1. It has cases in /hunt for empty, long, error and pending. Without those, it doesn't exist.
  2. It survives 320px without horizontal scroll and 200% zoom without losing the button.
  3. It works in both themes — and none of its colors is a literal.
  4. It gets visible focus from the keyboard, in the order people expect.
  5. The error is announced, not just painted red.
  6. It's in a commit you've read, with a message that explains why.
  7. It's been seen on a preview URL, on someone else's actual phone.

Ship

One push, one URL.

Vite's npm run build outputs static files. Vercel detects the project on its own, publishes to the CDN and hands back an address — in seconds, not in a meeting with infrastructure.

# the repository
gh repo create my-app --private --source=. --push

# connect and publish
npm i -g vercel
vercel link

# the keys, per environment
vercel env add VITE_SUPABASE_URL production
vercel env add VITE_SUPABASE_ANON_KEY production

vercel --prod
Preview per branch
Every branch and every PR gets its own permanent URL. You send the link on WhatsApp and the person opens it on their phone. It's the most honest design review there is.
Production & rollback
main becomes production on every push. If something broke, the previous deploy comes back in one click — Git's history and Vercel's history are the same history.
The route that 404s
A SPA with client-side routes needs a rewrite to /index.html in vercel.json, or /hunt only works when you navigate to it from inside.

Four steps

From prototype to a real application.

Step 1 — the setup
git init before the first screen. Vite + Tailwind + tokens.css + CLAUDE.md. One static screen, one branch, one Vercel preview to prove the whole path works.
Step 2 — the data
supabase start on your machine. One table, one read, one write. Email login. The first screen that changes depending on who's logged in.
Step 3 — the hunt
The /hunt route and the first eight cases for your text field. The first overflow found at 320px. The first fix you wouldn't have seen in Figma.
Step 4 — your ceiling
RLS on the tables, versioned migrations, domain pointed, vercel --prod. And the matrix covering the five components you use most.

No step depends on learning everything. Each one delivers one thing that works and gets out of the way.

The links

Every piece has a front door.

Node.js
The runtime everything rides on — nodejs.org
Vite
The dev server and the build — vite.dev
React
The UI library underneath — react.dev
Tailwind
Utility CSS on top of your tokens — tailwindcss.com
Supabase
Postgres, auth, storage and RLS — supabase.com
Claude Code
The builder in your terminal — claude.com · docs at code.claude.com/docs
Git + GitHub
History and the remote — git-scm.com · github.com
Vercel
Deploys, previews, rollback — vercel.com

End

A good tool is
one you can open.

Vite opens. Supabase opens. Git opens. The browser opens. And when you can open it, you can fix it — including the ninety-nine situations nobody designed.

Wellington Mota — Product Engineering

git init npm run dev localhost:5173/hunt vercel --prod
Architecture for Designers navigate O overview G grid P print