design-folio
v0.2.0
Published
A design presentation system for vibe-coded prototypes
Maintainers
Readme
Design Folio
A design presentation system for vibe-coded prototypes. Bridges the gap between how designers build prototypes with Claude and how they share and present design work.
What it does
Folio wraps your prototype in a viewer with:
- Prototype tab — your prototype at full size for testing interactions. No state picker, no design notes. Navigate using the links and UI in your prototype.
- Screens tab — all your screens laid out for design review. Grid view (4-up with pan/zoom) and gallery view (single screen with design notes). This is where you examine design decisions.
- Explorations tab — isolated UI elements being workshopped before incorporation into full screens. Compare alternatives side by side, mark winners, and link decisions to the screens where they appear. This is where the design process is made visible.
- Changelog — Claude-summarized history of what changed and when.
- Documents — links to Figma files, PRDs, research decks, design rationale docs, and other external context.
Setup
Folio is designed to be set up by the /folio Claude Code skill, which handles installation, configuration, and ongoing maintenance automatically. You shouldn't need to configure anything manually.
If you want to set it up manually:
npm install design-folio --save-devAdd to your Vite config:
import { folioPlugin } from 'design-folio/vite';
export default defineConfig({
plugins: [folioPlugin()],
});Create a folio.json at your project root:
{
"name": "Project Name",
"designer": "Your Name",
"description": "A short paragraph about the project.",
"viewport": "desktop",
"component": "./src/App.jsx",
"states": [
{
"key": "home",
"name": "1. Home",
"group": "Main",
"state": {},
"notes": "Design rationale for this screen."
}
]
}Configuration
All fields in folio.json are optional. Folio works with zero config by defaulting to ./src/App.jsx with a desktop viewport.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| name | string | "" | Project name shown in the header |
| designer | string | "" | Designer name shown in the header |
| description | string | "" | Project description shown in the prototype tab sidebar |
| viewport | string or object | "desktop" | "desktop", "wide-desktop", "tablet", "mobile", "responsive", or { width, height } |
| component | string | "./src/App.jsx" | Path to the prototype component |
| syncTheme | boolean | false | Pass Folio's light/dark theme to the prototype |
| prototypeUrl | string | "/" | Base URL for the prototype. When set, Folio renders screens as iframes instead of using React cloneElement — enabling support for any prototype (vanilla JS, multi-page HTML, etc.) |
| states | array | [] | Screen states with keys, names, groups, state objects, and notes |
| explorations | array | [] | Design explorations with alternatives, winners, and screen links |
| changelog | array | [] | Changelog entries grouped by date |
| links | array | [] | External document links |
Viewport presets
| Preset | Dimensions |
|--------|-----------|
| desktop | 1080 × 720 |
| wide-desktop | 1440 × 900 |
| tablet | 768 × 1024 |
| mobile | 375 × 812 |
| responsive | 100% width, auto height |
State objects
Each state in the states array drives a screen in the Screens tab.
For React prototypes (no prototypeUrl), the state object is passed as an appState prop via React.cloneElement:
{
"key": "home-empty",
"name": "2. Empty State",
"group": "Home",
"state": { "items": [], "loading": false },
"notes": "Shown before any data is loaded."
}For iframe-based prototypes (with prototypeUrl), use url and/or script to reach each state:
{
"key": "hierarchy-view",
"name": "2. Hierarchy View",
"group": "Map Views",
"script": "document.getElementById('btnHierarchy').click()",
"notes": "Org-chart mode showing reports-to relationships."
}| State field | Type | Description |
|-------------|------|-------------|
| key | string | Unique identifier (used in URL hash) |
| name | string | Display name shown in sidebar and screen title |
| group | string | Section heading in the Screens grid |
| state | object | Passed as appState prop to React prototypes |
| url | string | Page URL for this screen, overrides prototypeUrl (e.g., "deal.html" for multi-page prototypes) |
| script | string | JavaScript injected into the iframe after load to trigger the state |
| scriptDelay | number | Milliseconds to wait after iframe load before injecting script (default: 100) |
| notes | string | Design rationale shown in gallery view |
Explorations
Explorations let designers work on UI elements in isolation — trying different approaches to a search bar, a navigation pattern, a card layout — before incorporating the winner into full screens. Each exploration is a named design problem with multiple alternatives.
{
"explorations": [
{
"key": "search-bar",
"name": "Search Bar",
"description": "Search paradigms for the dashboard header",
"alternatives": [
{
"key": "full-width",
"name": "Full-width Bar",
"file": "./explorations/search-bar/full-width.html",
"width": 540,
"height": 80,
"notes": "Always-visible search field. Most discoverable."
}
],
"chosen": "full-width",
"rationale": "Tested best with non-technical stakeholders.",
"usedIn": ["home-populated"]
}
]
}| Exploration field | Type | Description |
|-------------------|------|-------------|
| key | string | Unique identifier |
| name | string | Display name (e.g., "Search Bar") |
| description | string | The design question being answered |
| alternatives | array | Each alternative has key, name, file (path to HTML), width, height, and notes |
| chosen | string or null | Key of the winning alternative |
| rationale | string or null | Why the winner was chosen |
| usedIn | array | State keys from states where the winner appears in the prototype |
Alternatives can be any web-renderable content — HTML, React components, vanilla JS. Each file is standalone and rendered in an iframe. Dimensions set how large the element renders in comparison and focused views.
The Explorations tab shows two views:
- Index — all explorations as a list, grouped by decision status (needs decision / decided), with a hero preview of the winner or first alternative
- Detail — drill into one exploration to see alternatives in comparison mode (side by side) or focused mode (single element on a dot grid canvas with design notes)
Explorations link bidirectionally to screens: the exploration detail shows which screens use the winner, and the Screens gallery shows which exploration informed a screen's design.
Design documents
The /folio skill generates three standardized markdown documents alongside folio.json:
docs/design-rationale.md— overall design philosophy, principles, visual languagedocs/decisions.md— decision log for every exploration: context, alternatives, outcomedocs/exploration-mapping.md— which exploration winners appear in which screens
These are linked in the Documents tab and kept updated as the design evolves.
How it works
Folio installs as a Vite plugin that serves your prototype component inside its viewer. Your prototype code is never modified — Folio wraps around it.
The /folio skill handles:
- Detecting your prototype type and structure
- Generating
folio.jsonwith states, explorations, notes, and changelog - Creating and maintaining design documents (rationale, decisions, mapping)
- Pulling the project description from your README
- Installing the Tailwind CSS dependency (required by the viewer)
- Keeping the config updated as you continue vibe coding
- Proactively asking for design rationale when it's missing
License
MIT
