Yoga studio booking clickable prototype
Five-screen mobile-first booking prototype built from Figma, with fake data, browser-saved bookings and no real payments.
reactvitetypescripttailwindreact-routerlocalstorage
Built 1 time, downloaded 0 times.
Install from your terminal
Run this in your project folder. It signs you in, shows what changes and asks before it overwrites a file.
npx claude-lab add yoga-studio-booking-clickable-prototype-22bd0fCLAUDE.md# Yoga studio booking prototype
## Purpose
This is a clickable prototype of a yoga studio booking app. The studio owner will test it on a phone. It is NOT a real product. Nothing here takes real money or stores real customer data.
## Stack
- React + Vite + TypeScript
- Tailwind CSS for styling
- React Router for the five screens
- No backend, no database, no real payments
- Bookings are saved in the browser (localStorage) so they survive a reload
## Screens and routes
- `/` class list
- `/class/:id` class details
- `/book/:id` booking form
- `/confirmation/:bookingId` booking confirmed
- `/my-bookings` list of saved bookings
## Commands
Run these from the project folder in a terminal.
- `npm install`: downloads the packages the project needs. Run once after copying the files.
- `npm run dev -- --host`: starts the app and makes it reachable from your phone on the same wifi.
- `npm run build`: makes a production build and checks that it compiles.
- `npm run lint`: checks code style and common mistakes.
- `npm run typecheck`: checks that the TypeScript types line up.
- `npm test`: runs the unit tests.
## Design rules
- The Figma design is the source of truth.
- Colours, fonts and spacing live as tokens in `tailwind.config.ts` and `src/styles/tokens.md`.
- Never hardcode hex colours or magic pixel values in components. Use a token.
- If a value is not in the tokens, add it to the tokens first and tell the owner.
- If the Figma MCP connection is not available, ask for screenshots or exported frames in `design/` and a list of colours and fonts. Do not guess colours.
## Layout
- Mobile first. The design width is 390px.
- Test at 390x844 and 360x740. Nothing may break at 360px.
- On desktop, just centre the phone-sized column. Do not build a desktop layout.
## Data and state
- Fake data lives only in `src/data/classes.ts` and `src/data/bookings.ts`.
- Components never contain inline mock data.
- Booking state is saved to localStorage so My Bookings works after a reload.
## Payment
- The payment step is a fake form. Any input succeeds.
- Show the label "Prototype - no real payment" on that step.
- Never ask for real card numbers, and never add a real payment library.
## Working style
- Workflow: explore, then plan, then code, then run the verify skill before saying a change is done.
- Use `/clear` between unrelated tasks and `/compact` at natural breaks.
- The user is new to coding. Explain each command in plain language before running it.
- Say what a change does in one or two plain sentences.
## Safety
- Never put secrets or real personal data in any file.
- If a key is ever needed, use a placeholder such as `YOUR_API_KEY`.
- Do not edit files outside this project folder.
## Reference files
@REVIEW.md
@SPEC.md
.claude/agents/test-runner.md---
name: test-runner
description: Runs the project's typecheck, lint, unit tests and Playwright smoke test, then returns only the failures. Use it to check work without filling the main conversation with test output.
model: haiku
tools: Bash, Read
maxTurns: 10
---
You run the project's checks and report only what failed. You do not fix code.
Run these commands in order, from the project root:
1. `npm run typecheck`
2. `npm run lint`
3. `npm test`
4. `npx playwright test smoke`
Rules:
- If a command does not exist in package.json, report it as "missing" and skip it. Do not invent a replacement.
- Do not stop at the first failure. Run every command so the report is complete.
- Read a file only if the error message points to it and you need a line of context.
- Never edit files, install packages or change settings.
Reply in one of two forms:
- If everything passes, reply with the single word `PASS`.
- Otherwise, list each failing command, then the first relevant error lines (at most 10 lines per command). Leave out passing commands and any progress output.
.claude/settings.json{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(npm install)",
"Bash(npm test*)",
"Bash(npx playwright test*)",
"Bash(git status)",
"Bash(git diff*)",
"Bash(git add*)",
"Bash(git commit*)"
],
"ask": [
"Bash(npm install *)",
"Bash(git push*)"
],
"deny": [
"Read(.env*)",
"Bash(rm -rf*)",
"Bash(curl*)",
"Bash(sudo*)"
],
"blockWritesOutsideWorkingDirectories": true
},
"sandbox": {
"enabled": true
},
"env": {
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku"
}
}
.claude/skills/figma-to-screen/SKILL.md---
name: figma-to-screen
description: Turn one Figma frame into a React screen for the yoga prototype, using design tokens and fake data, then compare it with the frame screenshot.
allowed-tools: Read, Write, Edit, Bash
---
# Figma frame to React screen
Use this process for one frame at a time. Figma is the source of truth for colours, fonts, and spacing.
## Inputs
- The frame name, for example "Class details".
- A PNG of the frame in `design/`, named after the frame, or Figma MCP data if Figma is connected.
- If neither exists, stop and ask the owner to export the frame. Do not guess colours or layout.
## Steps
1. **List the elements.** Write a numbered list of every element in the frame: text, role, size, colour, spacing, icon, and state (default, empty, full, error). Mark anything unclear as `TODO`.
2. **Map values to tokens.** For each colour, font size, radius, and spacing value, find the matching token in `src/styles/tokens.md` and `tailwind.config.ts`. Use the Tailwind classes that point at those tokens. Never write hex values in components.
3. **Handle missing tokens.** If a value is not in the tokens:
- add it to `tailwind.config.ts` and `src/styles/tokens.md` first,
- then use it in the component,
- and tell the owner which token you added and where its value came from.
If the value cannot be read from the design, list it under "Needs owner confirmation" in `tokens.md`, use the nearest existing token, and say so. Do not guess.
4. **Build the screen.**
- Edit the route component for this screen in `src/`.
- Keep the content in the 390px centred column.
- Make every tap target at least 44px tall and wide.
- Reuse the shared `Button`, `Card`, `Header`, and `BottomNav` components if they exist (Phase 2). Do not copy them.
5. **Add fake data.** Import data from `src/data/classes.ts` or `src/data/bookings.ts`. Never write mock arrays inside a component.
6. **Compare with the screenshot.** Run the design check from the verify skill: take a 390x844 screenshot of the running screen and compare it with the frame PNG.
7. **Verify.** Run the verify skill before saying the screen is done.
## Output
End with a short checklist. Keep it plain so the owner can read it.
```
Frame: Class details
Matches
- [x] Title size and weight
- [x] Book button colour (token: primary)
- [x] Card spacing (token: space-4)
Known differences
- [ ] Instructor avatar is a placeholder (no image in design/)
Needs owner confirmation
- Cancellation text: exact wording not in the frame
- Tokens added this run: secondary-text colour (value read from design/notes.md)
```
Rules:
- Do not finish a screen with unresolved `TODO` markers unless they are listed under "Needs owner confirmation".
- Do not add features that are not in the frame or in `SPEC.md`.
- Do not add packages. If a package seems necessary, ask first.
.claude/skills/verify/SKILL.md---
name: verify
description: Run before saying any change to the yoga prototype is done. Checks types, lint, unit tests, the build and a phone-sized booking smoke test.
allowed-tools: Bash, Read
---
# Verify the prototype
Use this skill before you tell the owner a change is finished. Never say "done" without a report from this skill.
## Before you start
1. Read `package.json` and check which scripts exist.
2. If a script is missing (common in early phases), write "not set up yet, skipped" for that step. Do not invent a replacement command.
## Steps, in order
Run each step even if an earlier one failed, so the report is complete.
1. Typecheck: `npm run typecheck`
2. Lint: `npm run lint`
3. Unit tests: `npm test`
4. Build: `npm run build`
5. Smoke run (below).
6. Design check (only if UI changed, see below).
## Smoke run
1. Start the dev server in the background: `npm run dev`. Wait until the local address responds.
2. Run `npx playwright test smoke`. The test uses a 390x844 viewport and must:
- open the class list,
- open one class's details,
- book it through the fake payment step,
- confirm the booking appears in My Bookings.
3. Stop the dev server when finished.
If the smoke test file does not exist yet (before Phase 4), mark it "not set up yet, skipped".
## Design check
Run this only when the change touched a `.tsx` file that renders a screen or component.
1. Take a 390x844 screenshot of each affected screen from the running app.
2. Compare it with the matching PNG in `design/`.
3. List every visible difference as a short bullet: colour, type size, spacing, corner radius, icon, or missing element.
4. Do not fix differences inside this skill. Report them, then let the change be fixed separately.
If `design/` has no matching PNG, say so and skip the comparison.
## Report format
Give one line per step. For a failure, show only the first relevant error line, not the whole log.
```
Verify report
- Typecheck: PASS
- Lint: FAIL - src/screens/ClassList.tsx:12 'price' is defined but never used
- Unit tests: PASS
- Build: not run (blocked by lint? no, build ran) PASS
- Smoke run (390x844): PASS
- Design check: 2 differences (see below)
- Book button corner radius looks 8px, Figma shows 12px
- Spacing between title and price is larger than the frame
```
Rules for the report:
- Say "PASS" only when the command exited successfully.
- If any step fails, the overall result is FAIL. Say what the owner should expect next, in plain words.
- Keep it short. The owner is new to coding, so explain each error in one plain sentence.
INSTALL.md# Install guide
## What this is
This is a clickable phone prototype of a yoga booking app, built from your Figma design. It uses fake data and takes no real payments, so you can test the booking flow on your phone.
## Before you start
You need three tools. Check each one in a terminal with the command shown.
1. Node.js (LTS version) from nodejs.org. Check: `node -v`
2. Git from git-scm.com. Check: `git --version`
3. Claude Code. Check: `claude --version`
A terminal is the app where you type commands. On a Mac, open Terminal. On Windows, use WSL2 (Windows Subsystem for Linux) for the best results.
## Set up the folder
1. Make a folder called `yoga-prototype`.
2. Open a terminal in that folder and run `git init`. This starts version history so you can save progress.
3. Copy the generated files into the folder, keeping their paths. The folder should contain `CLAUDE.md`, `SPEC.md`, `PLAN.md`, `REVIEW.md`, `INSTALL.md` and a `.claude/` folder.
## Add your design
1. Export each of the five Figma screens as a PNG. Save them in a folder called `design/`.
2. Create `design/notes.md`. Write down the colours (hex codes if you have them), the fonts and the corner radius.
3. If a value is unclear, leave it out. Claude will list it under "Needs owner confirmation" instead of guessing.
## Optional: connect Figma
Claude can read the design directly if Figma is connected. Ask Claude in the terminal to connect Figma, then follow the sign-in prompts. If you skip this, the PNG exports in `design/` are enough.
## Add plugins
Run these commands one at a time in your terminal. Each one adds a skill or tool to Claude.
```
claude plugin marketplace add anthropics/skills
claude plugin install frontend-design@anthropic-agent-skills
claude plugin install webapp-testing@anthropic-agent-skills
claude plugin install code-review@claude-plugins-official
claude plugin install commit-commands@claude-plugins-official
```
Inside Claude you can also type `/plugin` to browse what is available.
## Start the build
1. In the `yoga-prototype` folder, run `claude`. This opens Claude in the terminal.
2. Run `/init` only if `CLAUDE.md` is missing. Most people will not need it.
3. Paste the kickoff prompt below and press Enter.
```
/goal Phase 1 setup. Read CLAUDE.md, SPEC.md, PLAN.md and the images and notes in design/. End state: a Vite + React + TypeScript + Tailwind project exists with React Router and five placeholder routes (/, /class/:id, /book/:id, /confirmation/:bookingId, /my-bookings), each showing its screen name inside a 390px-wide centred column; design tokens extracted from design/ are in tailwind.config.ts and documented in src/styles/tokens.md, with any unreadable value listed under 'Needs owner confirmation'; a README.md has the commands. Check: run the verify skill, with npm run typecheck, npm run lint and npm run build all passing, and show the output. Constraints: no backend, no payments, no state libraries, no packages beyond Vite, React, React Router, Tailwind, Vitest and Playwright; ask before installing anything else; explain each command in one plain sentence; do not edit files outside this folder; no secrets. Stop after 25 turns and report what is done, what is not and what you need from me.
```
## See it on your phone
1. Make sure your phone and computer are on the same wifi.
2. In a terminal in the project folder, run `npm run dev -- --host`. This starts the app and makes it visible on your network.
3. Look for the line that says "Network" with an address like `https://example.com`.
4. Open that address in your phone's browser.
## Useful Claude commands
- `/clear` wipes the conversation. Use it between unrelated tasks.
- `/compact` shortens the conversation. Use it at natural breaks, such as the end of a phase.
- `/usage` shows where your tokens went.
## If something breaks
Ask Claude: "Run the verify skill and explain the first error in plain words." Claude will run the checks, show which step failed, and explain it without jargon.
## Sandbox and settings
- The project settings turn on the Bash sandbox. It limits what shell commands can touch.
- The sandbox works on macOS, Linux and WSL2. On native Windows, commands run without the sandbox, so use WSL2.
- Settings files live in three places:
- `~/.claude/settings.json` applies to you on every project.
- `.claude/settings.json` applies to this project and is committed to git.
- `.claude/settings.local.json` is for your personal changes and is not committed.
PLAN.md# Build plan
Five phases. Each ends with a `/goal` prompt. Claude keeps working until the condition is met or the turn limit is reached.
Before each phase, run `/clear` to start with a clean context. Run `/compact` at natural breaks inside a phase.
Each `/goal` evaluator only sees what Claude has shown in the conversation. Ask Claude to print the command output, not just say it passed.
## Phase 1: Setup and tokens
Scaffold the project with Vite, React, TypeScript and Tailwind. Add React Router with five placeholder routes. Extract design tokens from Figma or `design/`.
Goal:
```
/goal Phase 1 setup. Read CLAUDE.md, SPEC.md, PLAN.md and design/. End state: Vite + React + TypeScript + Tailwind project with React Router and five placeholder routes (/, /class/:id, /book/:id, /confirmation/:bookingId, /my-bookings), each showing its screen name in a 390px centred column. Design tokens (colours, fonts, radius, spacing) are in tailwind.config.ts and src/styles/tokens.md. Values you cannot read from design/ go under "Needs owner confirmation", not guessed. Check: npm run typecheck, npm run lint and npm run build all pass; print their output. Constraints: no backend, no payments, no state libraries, no new dependencies without asking, no files outside this folder, no secrets. Stop after 30 turns and report what is done, what is not, and what you need from me.
```
## Phase 2: Fake data and shared components
Add the fake data files, the localStorage booking store, and shared components: Button, Card, Header, BottomNav.
Goal:
```
/goal Phase 2 data and components. End state: src/data/classes.ts and src/data/bookings.ts exist with the Class and Booking shapes from SPEC.md. A localStorage booking store supports create, list, cancel, and spot adjustment. Shared components Button, Card, Header and BottomNav exist in src/components and use tokens only. Check: npm test passes with unit tests for the store (create reduces spots, cancel restores spots, full class rejects booking, data survives reload); print the output. Constraints: no new dependencies without asking (Vitest is allowed), no real payments, no backend, no mock data inside components. Stop after 30 turns and report.
```
## Phase 3: Class list and class details
Build the class list (/) and class details (/class/:id) to match the Figma frames.
Goal:
```
/goal Phase 3 class list and details. End state: / shows fake classes in cards, with a day selector and an empty state for days with no classes. /class/:id shows full details, a Book button, a disabled "Class full" state, and a friendly not-found for unknown ids. Both match the Figma screenshots in design/ and use tokens only. Check: npm run typecheck and npm run lint pass. Then write a screenshot comparison checklist in your reply (colours, type, radius, spacing, icons, at 390x844 and 360x740), listing any differences. Constraints: no new dependencies without asking, no hardcoded hex values, no real payments, no backend. Stop after 30 turns and report.
```
## Phase 4: Booking, confirmation and My bookings
Build the booking form (with the fake payment step), the confirmation screen, and My Bookings with cancel. Handle the full-class and not-found states.
Goal:
```
/goal Phase 4 booking flow. End state: /book/:id has name and email validation and a fake payment step labelled "Prototype - no real payment" where any input succeeds. Submitting saves a booking, reduces spotsLeft, and goes to /confirmation/:bookingId. /my-bookings lists saved bookings, cancels them and restores the spot, and shows the empty state. A full class blocks booking. Unknown booking ids show a friendly not-found. Check: an end-to-end Playwright test named smoke at 390x844 books a class and sees it in My Bookings. Run npx playwright test smoke and print the output. Constraints: no real card fields or real payment code, no new dependencies without asking (Playwright is allowed), no backend. Stop after 30 turns and report.
```
## Phase 5: Polish and share
Spacing pass against Figma, accessibility basics, and a way for the owner to open the prototype on a phone. Write README.md.
Goal:
```
/goal Phase 5 polish and share. End state: spacing matches Figma within the checklist from Phase 3. Tap targets are at least 44px. Text contrast meets WCAG AA for body text. README.md explains in plain language how to install, run with npm run dev -- --host, open the Network address on a phone on the same wifi, and run the tests. Optional: a preview deploy to Netlify or Vercel, if the owner asks for one. Check: npm run build passes and README.md explains phone access; print the build output. Constraints: no secrets, no real payments, no backend, no new dependencies without asking. Stop after 30 turns and report.
```
## After each phase
- Run the verify skill (`/verify` or ask Claude to run it) before moving on.
- Use `/usage` to see token use if a phase feels slow.
- If something breaks, ask Claude to explain the error in plain words before changing code.
REVIEW.md# Review rules
Use these rules when reviewing any change to this prototype. Flag anything that breaks them.
## Design fidelity
- Colours, fonts, corner radius and spacing come from tokens in `tailwind.config.ts`.
- No hardcoded hex values or magic spacing numbers in components.
- If a value is missing from the tokens, add it to the tokens first.
## Scope and safety
- No real payment code. The payment step is a fake form only.
- No external network calls from app code.
- No secrets or real personal data in any committed file.
## Data
- Fake data lives only in `src/data/`.
- Components never inline mock data.
- Any change to the booking store needs a unit test or a smoke check.
## Mobile and access
- Nothing breaks at 360px width. Check 390x844 too.
- Tap targets are at least 44px.
## Route states
- Data is local, so no loading states are needed.
- Every route handles empty and error states safely.
- An unknown class id shows a friendly not-found message.
## Code quality
- TypeScript strict mode is on.
- No `any` unless a comment explains why.
- Flag over-engineering: no state library, no backend, and no extra abstraction layers for a five-screen prototype.
SPEC.md# Yoga booking prototype spec
## Goal and non-goals
Goal: a clickable, phone-sized prototype of the yoga studio booking flow, built from the Figma design, with fake data.
Non-goals:
- No login or accounts.
- No real payments. The payment step is a fake form.
- No admin screens.
- No real backend or database.
- No email or notifications.
## User and success
- User: the studio owner, testing on their own phone.
- Success: the owner books a class and then sees it in My Bookings.
## Screens
Bottom nav (Classes, My Bookings) appears on screens 1 and 5.
### 1. Class list (/)
- Header with studio name placeholder. TODO: check the frame for the exact header.
- Day selector, 7 days across. TODO: check the frame for the selected state.
- Class cards: title, instructor, level, start time, duration, spots left, price.
- Full class shows a "Full" badge.
- Tap a card: go to /class/:id.
- Empty state: "No classes on this day."
### 2. Class details (/class/:id)
- Title, instructor, level, date and time, duration, spots left, price, description.
- Primary button "Book this class" goes to /book/:id.
- Full class: disabled button "Class full".
- Back button goes to /.
- Unknown id: friendly "Class not found" with a link back to /.
### 3. Booking (/book/:id)
- Class summary at the top.
- Form fields: name and email. Both required. Show an inline message if empty, or if email has no @.
- Fake payment step: any input succeeds. Show the label "Prototype - no real payment". Placeholder text says any value works. Never ask for real card details.
- Submit creates a booking, reduces spotsLeft by one, and goes to /confirmation/:bookingId.
- If the class became full before submit: show "Class full" and block submit.
### 4. Confirmation (/confirmation/:bookingId)
- Message "Booked", class title, date and time, name.
- "See my bookings" goes to /my-bookings. "Back to classes" goes to /.
- Unknown booking id: friendly "Booking not found" with a link back to /.
### 5. My bookings (/my-bookings)
- List of saved bookings: class title, date, time.
- Cancel button on each booking. Cancelling removes it and restores one spot.
- Empty state: "No bookings yet" with a button to /.
## Fake data shape
- Class: `{ id, title, instructor, level, startsAt, durationMin, spotsLeft, price, description }`
- `startsAt` is an ISO date-time string. `price` is a number. Currency is a TODO (see open questions).
- Booking: `{ id, classId, name, email, createdAt }`
- Classes live in `src/data/classes.ts`. Bookings live in `src/data/bookings.ts`.
- Saved bookings go to localStorage so they survive a reload.
## Flow rules
- A booking reduces the class's spotsLeft by one. The class list and details screens show the new count.
- A class with spotsLeft 0 shows a disabled "Class full" button and cannot be booked.
- Cancelling from My Bookings removes the booking and adds one spot back.
- Bookings persist across reloads.
- Price is shown but never charged.
## Empty states
- No bookings yet (My bookings).
- No classes on the selected day (Class list).
## Visual fidelity checklist
Compare each screen with its screenshot in `design/`.
- [ ] Colours come from tokens in tailwind.config.ts. No hardcoded hex values.
- [ ] Type sizes and weights match.
- [ ] Corner radius matches.
- [ ] Spacing matches.
- [ ] Icons match.
- [ ] Checked at 390x844 and 360x740.
## Open questions for the owner
- Studio name and logo.
- Real class names, instructors, levels, prices and currency.
- Class lengths and weekly schedule.
- Cancellation rules (how late, any fee).
## Acceptance checklist
The verify skill ticks these off.
- [ ] `npm run typecheck` passes.
- [ ] `npm run lint` passes.
- [ ] `npm test` passes.
- [ ] `npm run build` passes.
- [ ] Smoke test books a class and sees it in My Bookings at 390x844.
- [ ] Cancelling restores the spot.
- [ ] A full class shows a disabled button.
- [ ] Unknown class and booking ids show a not-found message.
- [ ] Both empty states render.
- [ ] Visual checklist is done, or differences are listed.