Showcase

Live demos.

Every demo shipped with these docs is a plain .aktion program — 111 of them, in four folders under docs/demos/. Each tile in the gallery boots the real runtime in an iframe, so you are looking at the program running, not a screenshot. Click one to open it full‑page, with a theme switcher and a View .aktion source button that hands the program straight to the playground.

Every demo

All 111 programs, grouped by folder — open a section to mount its previews. The per‑section counts are read from demos/manifest.json, so this list is whatever is on disk. Everything below the gallery explains how the demos are built and how to run them yourself.

What these demos are

One demo is one file. There is no build step, no JavaScript glue and no per‑demo HTML page: docs/demos/<folder>/<name>.aktion is the whole program, and a single runner shell renders whichever one its ?app= parameter names.

That is what makes them useful as examples. Nothing in a demo is a documentation‑only shortcut, so the text you read here is the text that runs — paste it into the playground or drop the file into your own src/ and it behaves the same.

$app(Column([
  PageHeader("Invoices", { subtitle: "Everything billed this quarter" }),
  Card([
    Table([
      Col("Invoice", ["INV-001", "INV-002"]),
      Col("Amount", ["$1,200", "$840"])
    ])
  ])
], { gap: "lg" }))

Programs read like that the whole way down: one $app(…) call composing components, with the data and helpers it references declared around it. The median demo is 91 lines — the smallest is 41, the largest 457.

Start here

Four programs cover most of what you will want to see first. Each link opens the full‑page runner in a new tab, so the gallery stays where it is.

Arrived from a guide and want the demo that exercises the primitive you just read about? This is the index:

GuideDemos that show it
HTTP Dictionary, Pokedex, Aktion Website ($http); Weather Dashboard, GitHub Explorer, Crypto Watchlist, News Reader, Show Finder, Cocktail Explorer, Currency Converter ($query) — ten mini‑apps in all.
Stores To-do List, Kanban Board, Budget Tracker, Invoice Builder — and 32 more. 36 of the 57 mini‑apps keep their data in a $store.
Routing Admin Dashboard and Storefront — the only two demos that route between pages.
Hooks components/forms, inputs, selection, utility — each holds per‑instance state with the $state hook.
Layout · sx components/layout for the primitives, any industry-specific/ surface for dense Grid work.
Themes Any demo — switch theme in the runner toolbar. Aktion Website and Saas Landing also call $theme(…) themselves.

No bundled demo writes to a REST API

The $http demos are all reads. To-do List looks like a CRUD app but persists through $store({ persist }) to localStorage. For create / update / delete over HTTP, scaffold the todos-app template instead — npm create aktion@latest my-app -- --template todos-app — which wires $http create, toggle, edit and delete against a REST endpoint.

The four categories

The folder a demo lives in tells you how much of an app it is. That is the entire taxonomy: a program lives in exactly one folder, and the folder name is the section heading you see in the gallery.

FolderProgramsWhat it demonstratesGuides it illustrates
mini-apps/ 57 Whole applications — trackers, dashboards, storefronts, API browsers, marketing pages. 36 keep data in a $store (34 of those persist it, 8 keep undo history), 10 fetch over the network, 10 build forms with $form, 2 route. Stores, HTTP, Routing
blocks/ 25 One drop‑in section per file — login card, pricing plans, checkout form, data table, command palette. No stores and no network, just local atoms, which makes them the smallest complete things to read. 8 raise a $toast. Components, Layout
components/ 14 Copy‑out function components grouped by kind — buttons, inputs, cards, charts, overlays, navigation, … Each file collects nine to twelve of them; 8 of the 14 use the $state hook. Components, Hooks
industry-specific/ 15 One vertical surface per file — finance, healthcare, SaaS, e‑commerce, AI, real estate, travel, … Each is a dense dashboard assembled from reusable blocks; 10 include charts and all 15 raise a $toast. Layout, Components

mini-apps/ is listed first everywhere because scripts/build-docs.mjs sorts it there; the other folders follow alphabetically and files inside a folder are sorted by name. The counts above come from demos/manifest.json, which that script regenerates from disk on every docs build.

Reading a demo

The demos are meant to be read, not just run. Three things make that quick: the source layout is the same in every file, the runner hands you the program in an editor, and the URLs are guessable.

The source layout

Read the first line before anything else. Every program calls $app(…) near the top and declares everything that call references below it — declarations hoist, so the head of the file is the page shape and the rest is detail.

$app(Center([Container([Column([header, addRow, list], { gap: "md" })], { size: "sm" })], { minHeight: "85vh" }))

todos = $store({
  persist: "todo.items",
  history: 50,
  items: [{ id: "t1", title: "Read the Aktion docs", done: false }],
  add:    (s, title) => { s.items = [...s.items, { id: $util.uuid(), title, done: false }] },
  toggle: (s, id) => { s.items = s.items.map(t => t.id == id ? { ...t, done: !t.done } : t) }
})

$draft = ""

header = PageHeader("To-do List", { subtitle: `${todos.items.length} tasks` })

addRow = Row([
  StackItem(Input("draft", { placeholder: "What needs doing?", value: $draft }), { grow: 1 }),
  Button("Add", { variant: "primary", onClick: () => { todos.add($draft); $draft = "" } })
], { gap: "sm", align: "end" })

list = Card([Column(todos.items.map(t => Row([
  Checkbox(`done-${t.id}`, { value: t.done, onChange: () => todos.toggle(t.id) }),
  Text(t.title)
], { gap: "sm", align: "center", key: t.id })), { gap: "sm" })])

That is mini-apps/todo-list.aktion cut down to its skeleton: header, addRow and list are used on line 1 and defined further down. 110 of the 111 programs put $app(…) in their first eight lines; mini-apps/aktion-website.aktion is the one that calls it lower, at line 33.

Most files also open with a comment header saying what the program is and what it is a starting point for — 110 of 111 carry one, and 55 add a // Uses: line naming the primitives. Because the demos are plain text, grep is a perfectly good index:

# Which demos fetch over the network?
grep -l '$query(' docs/demos/*/*.aktion

# Which demos route between pages?
grep -l '$router(' docs/demos/*/*.aktion

Eight programs answer the first question and two the second; count the four $http demos as well and ten of the 57 mini‑apps talk to a network, which is the figure The four categories reports.

The runner shell

One page, demos/index.html, renders every demo. It adds a thin toolbar above the program and nothing else:

ControlWhat it does
AppSwitches program without leaving the page, grouped by folder. It rewrites ?app= in the address bar as you go, so the URL stays shareable.
ThemeRe‑renders the program under any built‑in theme — light, dark, shadcn, shadcn-dark, mui, mui-dark, heroui, heroui-dark, signal, signal-dark, soft. The program itself never changes.
View .aktion sourceOpens the program in the playground as a #code= share link, ready to edit and re‑run. Nothing is uploaded — the source is gzipped into the URL fragment.

Errors are not swallowed. The runner listens for the element’s error event and prints parse and runtime messages in a red box above the program, so a demo you break while editing tells you where.

Deep‑linking a demo

Both URL shapes the gallery uses are ones you can write by hand — useful when a guide, an issue, or your own notes should point at one exact program:

demos/index.html?app=mini-apps/todo-list.aktion            // full page, with the toolbar
demos/index.html?app=blocks/login-card.aktion&embed=1      // chrome-less: the program only

A missing or unrecognised app falls back to mini-apps/aktion-website.aktion. embed only has to be present — any value hides the toolbar, which is exactly what the preview tiles load.

How the preview tiles work

The tiles under Every demo are not images. Each one mounts an iframe pointing at the embedded runner, which is why the gallery is careful about how many programs it boots at once.

Previews are look‑only

Pointer events are disabled inside a tile. Clicking opens the interactive full‑page demo in a new tab rather than pressing whatever control sits under the cursor. Hover auto‑scroll is the only way to see the rest of a long page from the gallery.

Running them locally

The gallery itself is static, but the runner imports the runtime from dist/, so a fresh checkout has to be built once before any demo renders. Two routes work.

From a clone, with Vite

git clone https://github.com/asfand-dev/aktion.git
cd aktion
npm install
npm run dev     # = npm run build && vite — builds dist/, then serves the repo root

Open the URL Vite prints (http://localhost:5173 unless the port is taken) and append /docs/live-demos.html. Editing a .aktion file and reloading is enough: the runner fetches each program with a cache‑busting ?ts= query, so you never get a stale copy.

As the deployable site

npm run build:docs              # rescans docs/demos/, then assembles ./site/
npx http-server site -p 4321    # or: npx serve site

Then open http://localhost:4321/live-demos.html. Because build:docs regenerates docs/demos/manifest.json before it copies anything, a program you drop into one of the four folders shows up in the gallery without editing the manifest by hand.

Opening the file directly will not work

Serve the folder over HTTP. The gallery and the runner both fetch("demos/manifest.json"), and browsers block that over file://. An empty gallery with no visible error is almost always this.

Next