Examples · Blog
A blog with comments on every story: 51 lines of AppSPEC
A journal with a featured carousel, topic filters, story pages rendered from Markdown, likes, responses on every story and related reading.
Running live in the sandbox with fixed demo data. Anything you add stays in this browser.
The state graph
3 screens, 1 wire
Screens are nodes and WIRE labels are edges. Screens in blue appear in the navigation; orange edges write a record on the way.
How it works
Walking through the spec
01Screens are states
3 screens, each a <STATE>. home, about appear in the navigation because they carry a NAV label. Children lay themselves out by containment: ROW, COL and GRID arrange them, and everything else stacks.
<STATE ID="home" NAV="Stories" ICON="book-open">
…
<STATE ID="read" WIDTH="760px">
…
<STATE ID="about" NAV="About" ICON="info" WIDTH="720px">02Screens connect by label
Each <WIRE> names an action and where it leads. A button, card or row that fires the label follows the wire; the clicked record travels with it. This app declares 1 wire.
<WIRE LABEL="read" TO="read" />03Data flows through the key-value pipe
Every input publishes its value into the pipe under its ID, and every screen can read the pipe with {placeholders}. No wire writes a record here; the pipe drives filtering and display, and comments and likes are stored by the social components themselves.
<CATEGORY-NAV ID="topic" SRC="articles" FIELD="category" />
…
<TEXT STYLE="eyebrow" TEXT="{record.category}" />
<TEXT STYLE="display" TEXT="{record.title}" />
<TEXT STYLE="lead" TEXT="{record.excerpt}" />
…
<AVATAR NAME="{record.author}" />
<TEXT STYLE="small" TEXT="{record.author} · {record.date|date} · {record.minutes} min read" />04Social components attach themselves
COMMENT-THREAD, LIKES, RELATED bind to the record on screen through its GUID (for example articles/…). The spec never says which record: the engine supplies it, so the same tags work unchanged on a video, a product or a story.
<ROW><LIKES LABEL="found this useful" /></ROW>
…
<COMMENT-THREAD TITLE="Responses" PLACEHOLDER="Share a thought…" />
…
<RELATED BY="category,tags" HEADING="Keep reading" THUMB="cover" META="{category} · {minutes} min read" LIMIT="3" ACTION="read" />05One attribute sets the look
THEME="claude" sets every colour, radius and typeface. Change the word and the whole app restyles; all 31 themes work with every component.
<APP ID="blog" NAME="Field Notes" THEME="claude" LOGO="feather" WIDTH="1080px">What this would typically take
The pieces a conventional stack needs
A typical implementation of the same app assembles these parts, each with its own code, libraries and configuration. The size depends on the team and the tools, so we do not quote a number for it.
Here the same app is one 51-line document built from general-purpose components: ARTICLE, AVATAR, BUTTON, CARDS, CAROUSEL, CATEGORY-NAV, COMMENT-THREAD, DIVIDER, HERO, IMG, LIKES, RELATED, TEXT.
| Routing | Routes for the index, each story and the about page. |
|---|---|
| Content rendering | A safe Markdown renderer and typography for long-form text. |
| Comments | A comments model and API per story, and the form to post. |
| Discovery | A carousel, topic filters and a related-stories query. |
| Build tooling | A bundler or static-site generator and its configuration. |
The full specblog.appspec · 51 lines
<APP ID="blog" NAME="Field Notes" THEME="claude" LOGO="feather" WIDTH="1080px">
<DATA SRC="api/" />
<STATE ID="home" NAV="Stories" ICON="book-open">
<HERO EYEBROW="The AppSPHERE journal" TITLE="Notes on building applications by describing them"
SUBTITLE="Short essays on declarative apps, the graph behind them and where the idea came from." />
<CAROUSEL SRC="articles" IMAGE="cover" TITLE="title" SUBTITLE="excerpt" LIMIT="3" HEIGHT="340px" ACTION="read" />
<ROW JUSTIFY="between">
<TEXT STYLE="h2" TEXT="All stories" />
<CATEGORY-NAV ID="topic" SRC="articles" FIELD="category" />
</ROW>
<CARDS SRC="articles" WHERE="category={topic}" SORT="date desc" COLS="3" IMAGE="cover" RATIO="1200/630"
TITLE="title" BODY="excerpt" META="{category} · {minutes} min read" ACTION="read" />
</STATE>
<STATE ID="read" WIDTH="760px">
<BUTTON STYLE="ghost" SIZE="sm" ICON="arrow-left" LABEL="All stories" ACTION="back" />
<COL GAP="12px">
<TEXT STYLE="eyebrow" TEXT="{record.category}" />
<TEXT STYLE="display" TEXT="{record.title}" />
<TEXT STYLE="lead" TEXT="{record.excerpt}" />
<ROW GAP="10px">
<AVATAR NAME="{record.author}" />
<TEXT STYLE="small" TEXT="{record.author} · {record.date|date} · {record.minutes} min read" />
</ROW>
</COL>
<IMG BIND="record.cover" RATIO="1200/630" RADIUS="16px" />
<ARTICLE BIND="record.body" />
<ROW><LIKES LABEL="found this useful" /></ROW>
<DIVIDER />
<COMMENT-THREAD TITLE="Responses" PLACEHOLDER="Share a thought…" />
<DIVIDER />
<RELATED BY="category,tags" HEADING="Keep reading" THUMB="cover" META="{category} · {minutes} min read" LIMIT="3" ACTION="read" />
</STATE>
<STATE ID="about" NAV="About" ICON="info" WIDTH="720px">
<TEXT STYLE="h1" TEXT="About this journal" />
<ARTICLE>
Field Notes is a demo blog written entirely in AppSPEC. Every page you see is one of three states in a single file.
The comment thread under each story is the **same component** the video and store demos use. It is not told which story it belongs to; it binds to the record the page is showing.
- Stories come from a read-only snapshot
- Your likes and responses stay in your browser
</ARTICLE>
</STATE>
<WIRE LABEL="read" TO="read" />
</APP>