vibe-prototypes
A lightweight, zero-build prototype server for the Let's Vibe wellness platform. It serves design-system docs, standalone HTML prototypes, and source files from one place — markdown is rendered to clean HTML, code files are syntax-highlighted on the fly, and everything else is served statically with a browsable directory index.
Use it to explore the Living Sanctuary brand system (letsvibe-brand/), demo interactive mockups (prototypes/), and read code without leaving the browser.
New here, or not technical? Open the home page and follow Start here: copy-ready prompts for Claude to create a prototype or a new version, with no coding. Source:
getting-started/prompts.json. Claude Code users can also run the shortcuts in.claude/commands/:/get-latest,/new-prototype,/new-version,/check-standardsand/ship(each is explained with an example on the home page).
AI agents and AI-assisted contributors: read and follow
AGENTS.mdbefore generating or changing anything in this repo. It holds the mandatory structure, brand and deployment rules.
Quick start
pnpm install # install dependencies (pnpm@10.12.4)
pnpm start # or: node app.js
Then open http://localhost:3000.
No build step, no environment variables, no config — node app.js serves the repo root on port 3000.
Deploying to Vercel
Import the repo in Vercel with no extra settings. vercel.json routes every request to api/index.js, which re-exports the Express app from app.js, so markdown rendering, view-source and the directory tree behave exactly as they do locally. includeFiles bundles prototypes/ and letsvibe-brand/ into the function. New top-level folders must be added to that glob.
Useful routes
| URL | What you get |
|---|---|
/ |
Browsable directory index of the repo |
/letsvibe-brand/README.md |
Brand system quick-start (rendered markdown) |
/letsvibe-brand/brand/BRAND_GUIDELINES.md |
Full brand & design guidelines |
/letsvibe-brand/docs/index.html |
Interactive styleguide |
/prototypes/wellness-hub/v8 |
Wellness Hub interactive prototype (latest) |
/prototypes/wellness-hub/v7 |
Wellness Hub — previous version |
/letsvibe-brand/tokens/tokens.json |
Any code file → syntax-highlighted view |
How it works
app.js is an Express 5 server with three layered middleware, applied in order:
- Markdown rendering — any
GET/HEADrequest for a*.mdfile is parsed withmarked(code blocks highlighted viahighlight.js), sanitized withDOMPurify(bound to a sharedjsdomwindow), and wrapped in a GitHub-style page with light/dark themes. - View source — navigating directly to a developer file (
.js,.ts,.json,.css,.scss,.yml, and ~20 more) returns a syntax-highlighted page. This is gated on theSec-Fetch-Dest: documentheader, so the same file requested as a page asset (<link>,<script>,fetch) still serves raw — keeping the live prototypes working..html/.htmare intentionally excluded so prototypes render instead of showing source. - Static serving — everything else is served by
express.static, withserve-indexproviding a directory browser (withnode_moduleshidden).
Path traversal is guarded by safeResolve, which rejects any request that resolves outside the repo root.
Project structure
vibe-prototypes/
├── app.js # Express server (markdown render + view-source + static)
├── package.json # deps; `start` script
├── CHANGELOG.md # Keep a Changelog + SemVer
├── letsvibe-brand/ # "Living Sanctuary" design system
│ ├── README.md # brand system quick-start
│ ├── CHANGELOG.md
│ ├── brand/
│ │ └── BRAND_GUIDELINES.md # voice, color, type, components, a11y, do/don'ts
│ ├── tokens/ # design tokens in 5 formats:
│ │ ├── tokens.json # W3C design tokens
│ │ ├── tokens.css # CSS custom properties
│ │ ├── tokens.scss # SCSS variables + maps
│ │ ├── tokens.ts # TypeScript (React / React Native)
│ │ └── tailwind.preset.js # Tailwind preset
│ ├── css/
│ │ ├── base.css # resets, fonts, focus, reduced-motion
│ │ └── components.css # orb, buttons, chips, cards, media helpers
│ └── docs/index.html # interactive styleguide
└── prototypes/ # versioned interactive prototypes
├── partners/ # v1/
├── vibe-landing/ # v1/
└── wellness-hub/ # v7/, v8/ (latest) — each version has its own index.html
└── index.html # standalone interactive prototype
Stack
- Runtime: Node.js (CommonJS) + Express 5
- Package manager: pnpm 10.12.4
- Markdown:
marked+marked-highlight - Highlighting:
highlight.js(GitHub light/dark themes) - Sanitization:
dompurify+jsdom - Static index:
serve-index
No bundler, no test suite, and no framework — by design. This repo is a self-contained reference/exploration tool and does not depend on the other apps in the monorepo.
Adding & generating prototypes
This server is a drop-in static host: there is no build step, no prototype registry, no environment variables, and no SPA history-fallback. You add a prototype by putting a file on disk; you run it by starting the server; you generate one by handing the prompt below to an AI. The single fact that drives every routing decision: unknown URL paths are never rewritten to index.html, so a deep link or reload only works if it maps to a real file on disk.
1. How to add a prototype
Prototypes live under a name + version folder and are served at the matching URL — the path on disk is the route:
prototypes/<name>/<version>/index.html
| On disk | Served at |
|---|---|
prototypes/wellness-hub/v7/index.html |
http://localhost:3000/prototypes/wellness-hub/v7 |
prototypes/<name>/v1/index.html |
http://localhost:3000/prototypes/<name>/v1 |
Conventions
<name>— kebab-case, the prototype's stable name (e.g.wellness-hub,onboarding-flow).<version>—v1,v2, … Bump the version for a new iteration; never edit a shipped version in place (a convention, not a server rule), so earlier states stay deep-linkable for review and comparison.index.htmlis the entry screen. Additional real.htmlfiles (e.g.step-2.html) live beside it (see routing note below).
No registration, no build. Drop the folder in and it is served immediately — there is nothing to import, register, or compile.
Find it via the directory index. Open http://localhost:3000/ for the browsable index (powered by serve-index, node_modules hidden), then drill into prototypes/ → <name> → <version>. Any .md you add renders as a styled page; any source file (.js, .css, .json, .ts, …) opens as a syntax-highlighted "view source" page when you navigate to it directly, but the same file still serves raw when loaded as a page asset (<link> / <script> / fetch), so your prototype keeps working. .html / .htm are excluded from view-source so prototypes render. (More on this in How it works.)
Brand tokens are picked up automatically — if you reference them. The "Living Sanctuary" design system lives in letsvibe-brand/. From a prototypes/<name>/<version>/ folder, link the tokens with a relative path three levels up to the repo root:
<!-- from prototypes/<name>/<version>/index.html -->
<link rel="stylesheet" href="../../../letsvibe-brand/tokens/tokens.css">
<link rel="stylesheet" href="../../../letsvibe-brand/css/base.css">
<link rel="stylesheet" href="../../../letsvibe-brand/css/components.css">
These load raw because they are page assets, not direct navigation (the view-source highlighter only triggers on Sec-Fetch-Dest: document). So var(--pine), var(--citrine-soft), var(--font-display), etc. resolve as normal stylesheets. For a fully portable single file you may instead copy the token :root block inline into a <style>. Either way, consume the tokens rather than hard-coding hex, and honor prefers-reduced-motion.
⚠️ Routing constraint — read before choosing navigation. This server has no catch-all /
history.pushStatefallback. Unknown URLs are not rewritten toindex.html, so a client-side router that pushes sub-paths like/flow/step-2will 404 on reload or deep-link — the server looks for a file at that path and finds none.
| Option | How | Real browser history? | Deep-link + reload? | Best for |
|---|---|---|---|---|
| (a) Multi-page | Separate real .html files linked with <a href> |
Yes (native) | Yes — every screen is a real file | The default. Simplest and most robust here. |
| (b) Hash routing | One file; routes live in the fragment (#/step-2) driven by hashchange / popstate |
Yes — each hash is a history entry | Yes — the fragment travels with the URL; the server only ever serves the one file | A single-file SPA feel with no server changes. |
(c) pushState |
Only if every pushed path maps to a real served file | Yes | Only for paths backed by real files | Avoid unless every path is a real file. |
Do not use
pushStateto paths that have no backing file. It otherwise requires editingapp.jsto add an SPA fallback — out of scope. When in doubt, use (a) or (b): both produce genuine Back/Forward history and reloadable, shareable URLs on this static server.
2. How to run it locally
See Quick start for the full story; the essentials:
pnpm install # pnpm@10.12.4
pnpm start # or: node app.js
Then open http://localhost:3000 (port 3000, serves the repo root) and navigate to prototypes/<name>/<version>. No build, no env vars, no config.
3. AI prompt template — generate a handoff-ready prototype
Paste the block below into Claude (or another capable AI), fill the <PLACEHOLDERS>, and you'll get a self-contained prototype that drops onto this server and is engineered for a Creative Technologist to fold into a real codebase — explicit data seams, a typed state model, marked component boundaries, and a machine-readable flow map.
You are building a self-contained interactive prototype for the "Let's Vibe" wellness
platform. It will be dropped, with NO build step, onto a static Express server that serves
the repo root and the file's path AS its URL. Follow every constraint below exactly.
PROTOTYPE
- Name: <PROTOTYPE_NAME> (kebab-case, e.g. onboarding-flow)
- Version: <VERSION> (e.g. v1)
- Demonstrates: <WHAT IT DEMONSTRATES> (the product story / value this proves)
- Key screens / flow: <KEY SCREENS / FLOW> (e.g. Welcome -> Goals -> Plan -> Confirmation)
OUTPUT LOCATION (the folder IS the route — do not change this shape)
file on disk: prototypes/<PROTOTYPE_NAME>/<VERSION>/index.html
served at: http://localhost:3000/prototypes/<PROTOTYPE_NAME>/<VERSION>
(the directory URL serves its index.html — no /index.html needed)
...plus any sibling .html screens and a FLOW.md, all inside that same folder.
HARD SERVER CONSTRAINTS (the server has NONE of these: build step, bundler, env vars,
config, prototype registry, or SPA history fallback / catch-all route)
- There is NO catch-all: an unknown URL is NOT rewritten to index.html. So:
* DO use MULTI-PAGE navigation (real sibling .html files linked with <a href>), OR
HASH routing (#/screen with hashchange + popstate).
* DO NOT use history.pushState to virtual sub-paths — they 404 on reload/deep-link.
- Follow AGENTS.md "URLs, browser history and deep links": it is mandatory.
- Every screen MUST produce a REAL browser history entry, so Back/Forward work and EVERY
screen is independently deep-linkable AND reloadable. The state that defines "which
screen" must live in the URL (the .html file path, or the #hash) — never in memory only.
- ACCESSIBLE AND RESPONSIVE ARE MANDATORY: follow AGENTS.md section 5 (WCAG 2.2 AA, mobile-first,
no horizontal scroll from 320px). Do not return a prototype that fails either.
- Self-contained: vanilla HTML/CSS/JS, no npm install, no CDN that could go offline
(inline small deps). It must run by opening its URL on this server, nothing else.
DESIGN TOKENS — "Living Sanctuary" (consume them; do not invent colors/fonts)
- Reference them with (3 levels up from the version folder; loads raw as an asset):
<link rel="stylesheet" href="../../../letsvibe-brand/tokens/tokens.css">
<link rel="stylesheet" href="../../../letsvibe-brand/css/base.css">
<link rel="stylesheet" href="../../../letsvibe-brand/css/components.css">
For maximum portability you MAY instead copy the token :root block inline.
- Use the token vars, e.g.:
surfaces: --mist / --paper text: --ink / --ink-soft
brand greens (carry the design): --sage / --sage-deep / --pine / --pine-2
spotlight: --citrine / --citrine-soft (ONE gold spotlight per screen — never more)
type: --font-display (Fraunces, headings) / --font-body (Hanken Grotesk, text)
--s-* spacing, --r-* radius, --dur-* / --ease motion.
Motion is calm and reactive: ALWAYS use CSS transitions (JS only toggles a class/attribute), with the
--dur-*/--ease tokens, and MUST honor @media (prefers-reduced-motion: reduce).
LIGHT AND DARK MODE ARE REQUIRED (see AGENTS.md "Light and dark mode (required)"): declare
color-scheme light dark, follow prefers-color-scheme using the token mapping (mist/paper/ink/ink-soft in
light; ink/pine-2/on-dark/on-dark-soft in dark; CTA uses the -on-dark tokens in dark). Design the dark
theme, do not just invert. No hardcoded hex.
HANDOFF-READY — this is the point of the deliverable. A Creative Technologist must be able
to lift this into a production React/Vue codebase with the seams already drawn:
1. DATA SEAMS (mock vs real) — isolate ALL fake data in ONE place per screen, a clearly
labeled block:
// === MOCK DATA BOUNDARY — replace with real API/props ===
// shape: { ...the exact object/array shape a real source must return }
// source: GET /api/<resource> (or: prop passed from parent)
// replace: swap the literal below for a fetch()/prop; nothing else changes
No fake data may be inlined into markup or logic — only read through these boundaries,
so the swap is a single, obvious edit.
2. STATE MODEL — declare ONE explicit state object at the top of the script with typed
fields (use JSDoc @typedef), documenting which fields are URL-derived (the source of
truth for the current screen) vs ephemeral UI state. Show the single function that maps
URL -> state and the single function that renders state -> DOM. No hidden globals.
3. COMPONENT BOUNDARIES — build each screen and each reusable piece (card, header, stepper,
button group) as a small named factory/function returning its markup, each preceded by:
// <ComponentName> — props: { ... } | future: maps to a <ComponentName> component
so the React/Vue extraction is mechanical.
4. INTEGRATION POINTS — at the top of index.html, a comment block listing every external
thing this prototype would touch in production: API endpoints (method + path + purpose),
auth/identity assumptions, analytics events fired, and any platform capabilities faked.
5. "WHAT IS FAKED VS REAL" — a short table (in FLOW.md AND as a top-of-file comment) with
columns: Concern | Faked here | Real source in production | Where to wire it.
FLOW MAP (so an AI agent can crawl and replay the journey)
- Create prototypes/<PROTOTYPE_NAME>/<VERSION>/FLOW.md containing:
* A route -> screen -> state table: the semantic URL of each screen (real .html path or
#hash), the screen's human name, what state/URL params it reads, and its exit links
(which screens it can navigate to). Every screen reachable from index.html.
* A 3-6 line prose FLOW description of the journey, its goal, and decision points.
* The "faked vs real" table from item 5.
- Mirror a compact version of the route -> screen -> state map as a comment block at the
TOP of index.html, so the flow is discoverable from the entry file alone.
- URLs MUST be semantic and self-describing (e.g. .../goals.html or #/goals), so an agent
can deep-link to and replay any step. Example shape:
/prototypes/<name>/<VERSION>/ -> Welcome -> { step: "welcome" }
/prototypes/<name>/<VERSION>/#/intake -> Intake -> { step: "intake", answers: {} }
/prototypes/<name>/<VERSION>/summary.html -> Summary -> { step: "summary" }
ACCEPTANCE CHECK (verify before returning)
[ ] Opening the entry URL and EVERY screen URL directly (cold reload) renders correctly.
[ ] Browser Back/Forward move through screens in the right order.
[ ] No history.pushState to a path without a matching real file.
[ ] URLs / HISTORY / DEEP LINKS (required): every view, step, tab, detail and meaningful modal has
its own URL (real .html file or #/hash route; query params inside the hash for filters/sort/
page). Opening any route cold or reloading lands exactly on that view (no bounce through
home). Navigation PUSHES history; in-view refinements and auto-redirects REPLACE it. Links
are real <a href>. Signed-out deep links go to sign-in and return to the requested route.
Unknown routes show a not-found view. document.title, focus and scroll update per
navigation. Never put secrets or personal data in a URL. All routes + sample deep links are
listed in FLOW.md and at the top of index.html.
[ ] All mock data sits behind labeled MOCK DATA BOUNDARY blocks; markup/logic read only
through them.
[ ] Tokens used for all color/type/spacing; exactly one citrine/gold spotlight per screen;
prefers-reduced-motion honored.
[ ] LIGHT AND DARK MODE (required): both designed with the token mapping, color-scheme declared, every
screen and deep link checked and axe-scanned in BOTH modes, no flash of the wrong theme, CTA rule
and AA contrast hold in both.
[ ] FLOW.md exists with the route->screen->state table, prose flow, and faked-vs-real
table; index.html repeats the route map at the top.
[ ] ACCESSIBILITY (required, WCAG 2.2 AA): semantic landmarks (one <main>, one <h1>, ordered
headings, <html lang>), real <button>/<a> elements, visible :focus-visible on every
control, full keyboard operation with no traps, labels for every field, alt text,
aria-live for dynamic updates, AA contrast in light AND dark, no color-only meaning,
zoom never disabled.
[ ] RESPONSIVE (required): viewport meta tag, mobile-first, no horizontal scroll from 320px,
fluid layout (grid/flex, clamp, rem), verified at 320 / 375 / 768 / 1024 / 1280 px,
works at 200% text size, touch targets >= 44px.
DELIVERABLES
prototypes/<PROTOTYPE_NAME>/<VERSION>/index.html (entry + top-of-file flow/integration map)
prototypes/<PROTOTYPE_NAME>/<VERSION>/<screen>.html (one per screen, if multi-page)
prototypes/<PROTOTYPE_NAME>/<VERSION>/FLOW.md (flow map + faked-vs-real table)
prototypes/<PROTOTYPE_NAME>/<VERSION>/CHANGELOG.md (changes from previous versions and updates made)
- State the routing strategy you chose (multi-page or hash) and why, at the top of FLOW.md.
GIT
- Repository location is 'https://github.com/HighestVibrations/vibe-prototypes'
- use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit format
- IF there is a previous version ensure CHANGELOG.md created for the new version speaks to changes from previous version.
After the AI generates the files, drop the folder under prototypes/, run pnpm start, and open http://localhost:3000/prototypes/<name>/<version>. Reload each screen and exercise Back/Forward to confirm the routing constraint is respected before handing off.