✅ Readable by AI Models
✅ Readable by Humans
✅ Changes viewable in Diffs
✅ Nothing lost between sessions
✅ Update live previews as you or a model make changes
Wirecannon lets you define UI wireframes as plain text files that are readable by humans, diffable in pull requests, and natively understood by AI models. Give a model a natural language instruction — it finds the right component, makes the change, and returns updated DSL. No clicking. No exporting. No context lost between sessions.
Existing wireframing tools were designed before AI models existed. They produce binary files, proprietary formats, or visual state that a model cannot read, address, or reason about. When you want a model to help iterate on a design, you're back to describing the layout in prose and hoping it understands.
Wirecannon is designed from the ground up for the way teams actually work now — where a model is part of the design conversation, not an afterthought.
Wireframes are .wcf files — plain text, square bracket syntax, two-space indentation.
[Header] # region=header anchor=true
[Row align:space-between]
[Image alt:"Acme logo"] # component=brand anchor=true
[Nav orientation:horizontal] # component=nav-primary role=navigation
[ButtonGroup align:right] # region=header.actions
[Button label:"Login" variant:ghost] # action=auth-login priority=secondary
[Button label:"Sign Up" variant:primary] # action=auth-signup priority=primary anchor=true
[Main] # region=main
[Section id:products] # region=main.products
[Grid cols:3 gap:md] # component=product-grid state=populated
[Card id:product-card] # component=product-card
[Image alt:"Product image"]
[Text content:"Product name" variant:subheading]
[Text content:"$0.00" variant:body]
[Badge label:"In Stock" variant:success]
[Button label:"Add to Cart" variant:primary] # action=cart-add priority=primary anchor=true
Every line carries two parallel streams of information:
| Stream | Read by | Purpose |
|---|---|---|
| DSL structure and attributes | Renderer | Produces the visual output |
# comment metadata |
Model | Resolves natural language instructions to component addresses |
The core rule: if it affects pixels, it belongs in the DSL. If it helps a model reason, it belongs in a comment.
Because every component carries a semantic address, a model can resolve instructions unambiguously:
"Move the action buttons in the header to be right aligned" → resolves
region=header.actions, modifiesalignattribute onButtonGroup
"Add a confirmation dialog before the checkout button" → creates
overlays/confirm-dialog.wcf, addstarget:confirm-dialogto the checkout button
"Show the empty state for the product grid" → resolves
component=product-grid, setsstate=empty
"Add a rating filter below price range in the sidebar" → resolves
region=sidebar.filters.price-range, inserts newSection id:ratingbelow it
The model edits the DSL. The render is a consequence, not the source of truth.
A Wirecannon project is a directory of .wcf files:
wireframes/
GRAMMAR.md ← language spec, always present, always read first
index.wcf ← required project manifest
screens/
home.wcf
product-list.wcf
checkout.wcf
overlays/
cart-drawer.wcf
auth-modal.wcf
index.wcf is the entry point. It declares every screen, every overlay, and every user flow. A model reads this before touching any other file.
Flows live in index.wcf and define the full application narrative — including branching paths and edge cases:
[Flow id:purchase label:"Purchase Flow"]
home → product-list → product-detail
| authenticated → checkout # happy-path=true
| unauthenticated → auth-modal → checkout # frequency=high
checkout
| confirmed → order-confirm # happy-path=true
| declined → checkout # retry=true
| error → support # frequency=low
A model given only index.wcf can answer questions like "what happens if payment fails?" or "where does the auth modal appear?" without reading every screen file.
npm install --save-dev @wirecannon/linter @wirecannon/rendereryour-project/
wireframes/
index.wcf
screens/
overlays/
GRAMMAR.md is fetched automatically by the model bootstrap — you do not need to copy it manually.
Create .claude/WIRECANNON.md in your project root:
This project uses Wirecannon (Wirecannon).
Before reading or editing any .wcf file:
1. Fetch the grammar spec: https://raw.githubusercontent.com/adm351/wirecannon/main/GRAMMAR.md
2. Read wireframes/index.wcf
The grammar defines all components, attributes, comment keys, validation rules,
and model instructions. Always use the live spec — do not rely on prior knowledge.Claude Code reads .claude/ automatically on startup. For other models, include this in your system prompt or inject it as context. The model always fetches the live grammar from GitHub, so it stays current with any language updates without changes to your repo.
{
"scripts": {
"lint:wireframes": "wirecannon-lint ./wireframes"
}
}The full language specification is in GRAMMAR.md. It defines:
- Three component tiers — Layout, Structure, Leaf — with explicit nesting rules
- Sparse attribute vocabulary — only attributes that would cause ambiguity without them
- Validated comment keys — a closed set of semantic metadata keys for model addressing
- 23 validation rules — enforced by the linter, checkable inline by any model
- 10 model instructions — explicit rules for how a model must behave when editing
.wcffiles
GRAMMAR.md is the contract between authors, models, linters, and renderers. It is versioned alongside the language.
| Package | Description | Status |
|---|---|---|
@wirecannon/linter |
Validates .wcf files against the grammar |
alpha |
@wirecannon/renderer |
Reference HTML renderer for visual verification | alpha |
@wirecannon/vscode |
VSCODE plugin for linting and previewing | alpha |
See examples/ecommerce for a complete project including:
index.wcfwith five user flows and fork syntaxscreens/product-list.wcf— header, sidebar filters, product gridoverlays/cart-drawer.wcf— drawer with populated and empty statesoverlays/auth-modal.wcf— modal with login and register sections
Sparse is better. If a renderer can infer it, the DSL does not say it. Every attribute in the grammar exists because omitting it would cause an ambiguous decision.
Closed vocabularies. Comment keys and component attributes are a fixed set. Unknown keys are grammar errors. This keeps the semantic layer trustworthy across sessions and contributors.
Model-first addressing. The comment system is not documentation — it is a semantic index. region, component, action, and anchor exist specifically so a model can locate any component from a natural language instruction without traversing the full tree.
No framework opinions. Wirecannon ends at a well-defined wireframe. React, Svelte, Vue, or anything else you want to generate from it is your decision, not the language's.
See CONTRIBUTING.md. Grammar changes require discussion — they are breaking changes. Linter and renderer implementations are the most impactful open contributions right now.
MIT — see LICENSE.