Create a new prototype
ProtoWiki uses file-based routing (unplugin-vue-router). Adding a
prototype is one folder + one file. There is no manifest, no router config,
no registration.
The 30-second version
mkdir -p src/prototypes/my-feature
touch src/prototypes/my-feature/index.vue
npm run dev
# open http://localhost:5173/my-feature
Then edit src/prototypes/my-feature/index.vue. A web prototype:
<script setup lang="ts">
definePage({
meta: {
// title + description: ask the author, or omit
category: 'prototype',
platform: 'web',
},
})
import ArticleLive from '@/components/article/ArticleLive.vue'
import ChromeWrapper from '@/components/chrome/ChromeWrapper.vue'
</script>
<template>
<ChromeWrapper>
<ArticleLive article="Albert Einstein" />
</ChromeWrapper>
</template>
An app prototype is the same file with the other platform and the other wrapper:
<script setup lang="ts">
definePage({
meta: {
category: 'prototype',
platform: 'app',
},
})
import AppChromeWrapper from '@/components/app/AppChromeWrapper.vue'
import ArticleLive from '@/components/article/ArticleLive.vue'
</script>
<template>
<AppChromeWrapper>
<ArticleLive app article="Albert Einstein" />
</AppChromeWrapper>
</template>
Reload — the home gallery (/) auto-lists the new entry, the route is live,
no other file needs to change.
Everything below applies to both platforms. For the app specifics — iOS vs
Android, app chrome, which template-app-* to copy — see
protowiki-app-prototyping.
Conventions
- Folder naming by kind. The folder name is the URL path (
src/prototypes/<name>/index.vue):- Templates (starters to copy):
template-<name>/— e.g.template-chrome,template-article-live. Setmeta.category: 'template'so the home gallery groups them below regular prototypes. - Examples (worked demos):
example-<name>/— e.g.example-event-worklist. Setmeta.category: 'example'. - Feature prototypes (real work in progress): unprefixed kebab-case — e.g.
edit-check,related-strip. Omitcategory(defaults to'prototype').
- Templates (starters to copy):
- One folder per prototype. Keep names short and unique.
- Gallery copy is human-written when present. Set
meta.titleandmeta.descriptiononly from author-provided copy — ask the author or omit; never AI-generate. See Gallery copy (never AI-generated). - Co-locate prototype-specific assets inside the folder
(
my-feature/data.json,my-feature/HelpModule.vue,my-feature/dashpage-fixtures.ts). Onlyindex.vuefiles become routes — co-located*.vuemodules are imported by the page (vite.config.tssetsfilePatterns: ['**/index']onsrc/prototypes/). Anything reusable belongs insrc/components/and gets a skill. - Wrap in the chrome for your platform unless the prototype is intentionally
a bare fragment. Most prototypes start with chrome → content.
- Web (
platform: 'web') —ChromeWrapper, which includes the defaultChromeHeader(with inlineSearchon desktop). Setusernamefor the Meta user link; replace#headeronly for fully custom chrome. - App (
platform: 'app') —AppChromeWrapper, which includes the app top bar and bottom icon nav. Seeprotowiki-app-prototyping.
- Web (
- Don't write per-prototype CSS for what Codex tokens already cover —
that's the
codex-usagediscipline that keeps prototypes looking like production.
Gallery copy (never AI-generated)
meta.title and meta.description are product copy for the home gallery
card — not implementation scaffolding.
Agents must not invent them. Two options:
- Ask the author for both strings before setting meta (preferred when the card will be shared).
- Omit them — the route still works; the gallery derives
titlefrom the folder name viaderiveTitleFromPath;descriptionis blank.
Use the author's exact wording when they provide it; only light copy-editing if they ask.
Never:
- AI-generate or guess title/description
- Use generic agent placeholders ("My feature prototype", "One-sentence pitch…")
- Keep a copied template's title/description on a new prototype without confirmation
Home gallery
Gallery cards are driven by flat definePage meta on each top-level
src/prototypes/<name>/index.vue:
category—'prototype'(default),'template', or'example'; controls which block the card appears in (prototypes first, then a divider, then templates+examples) and the gallery title prefix for templates and examples (Template:/Example:)platform—'web'(default) or'app'; shown as a chip on the gallery card (Web / App), and splits the Templates tab into a Web templates section followed by an App templates sectionorder— optional sort key within a block (lower first; default alphabetical)hidden— omit from gallery while keeping the route livespotlight— when any prototype is spotlighted, the gallery shows only spotlighted entries (DAW-style solo)
Full field reference, layout rules, and examples:
references/gallery-meta.md.
Common shapes
Web (platform: 'web')
| Goal | Composition |
|---|---|
| Article-style page with chrome (live) | <ChromeWrapper><ArticleLive article="…"/></ChromeWrapper> |
| Article-style page with committed snapshot fixture | <ChromeWrapper><ArticleSnapshot article="…"/></ChromeWrapper> |
| Article-style page with hand-written body HTML (no REST, no snapshot file) | <ChromeWrapper><ArticleCustom>…</ArticleCustom></ChromeWrapper> — canonical: src/prototypes/template-article-custom/; see protowiki-components → article.md (infobox classes, lead markup order) |
| Special-page-style page | <ChromeWrapper><SpecialPageWrapper title="…">…</SpecialPageWrapper></ChromeWrapper> |
| Newcomer homepage / dashboard | <ChromeWrapper :last-edited-notice="false"><SpecialPageWrapper title="Dashboard" help><Dashboard>…</Dashboard></SpecialPageWrapper></ChromeWrapper> — starter: template-dashboard/; full modules: template-homepage/ |
| Bare canvas with chrome | <ChromeWrapper>…</ChromeWrapper> |
| A/B preview, two themes side by side | Two <ChromeWrapper>s, one theme="light", one theme="dark" |
| Mobile-web preview embedded in a desktop page | <MobileWrapper><ChromeWrapper skin="mobile">…</ChromeWrapper></MobileWrapper> |
App (platform: 'app')
| Goal | Composition |
|---|---|
| App shell with header + bottom nav | <AppChromeWrapper>…</AppChromeWrapper> — starter: template-app-chrome/ |
| In-app article reader | <AppChromeWrapper><ArticleLive app article="…"/></AppChromeWrapper> — starter: template-app-article/ |
| In-app search | AppChromeHeader with a search-field item — starter: template-app-search/ |
| iOS vs Android differences | useIsIos() / [data-app-platform] — see protowiki-app-prototyping |
See protowiki-components for full
component docs and protowiki-components/references/composition-recipes.md
for more recipes.
What you don't need to do
- No router config.
unplugin-vue-routerreadssrc/prototypes/and generates the route table. Only**/indexfiles undersrc/prototypes/are routes; other co-located.vuefiles (e.g.HelpModule.vue) are normal imports. - No gallery edit.
src/prototypes/index.vuereads routemetaat runtime; new top-level folders appear automatically when theirdefinePagemeta is set. Onlysrc/prototypes/<name>/index.vueshows on the gallery — nestedsrc/prototypes/<proto>/<sub>/index.vueroutes are registered but listed only when linked from inside a prototype (e.g. mobile drill-down pages). - No skin/theme/platform setup.
<html data-skin>,<html data-theme>and<html data-app-platform>are all set at boot from URL params + viewport +prefers-color-scheme+ stored preferences. Codex tokens cascade through. - No build step for prototypes.
npm run devHMRs every change.
Sharing your prototype
When the design review is local: npm run dev, share the URL.
When you want a stable URL on GitHub Pages: push to main (CI builds and
deploys). Open a PR in your fork for an automatic preview URL on the PR.
See protowiki-deploy.
When to break the conventions
The conventions exist to keep the gallery uniform and the file tree
predictable. Break them when the prototype is genuinely different — e.g.,
nested routes (/edit-check/step-1, /edit-check/step-2) live as
src/prototypes/edit-check/step-1/index.vue etc. (any **/index under
src/prototypes/), and a folder-level layout file can sit at
src/prototypes/edit-check/index.vue if there's a shared shell. Nested
index.vue files do not appear on the home gallery — only the prototype root
does. Still set meta.title / meta.description on nested routes when useful
for devtools or direct URLs.