Tip
One import renders every block on this site.
Install
Section titled "Install"pnpmpnpm add comark @comark/html @onmax/comarknpmnpm install comark @comark/html @onmax/comarkRender
Section titled "Render"Pass
onmax()to the renderer.
Annotated screenshots, code changes, charts, and more, written in Markdown. Install every plugin with one package, or each on its own.
Tip
One import renders every block on this site.
pnpm add comark @comark/html @onmax/comarknpm install comark @comark/html @onmax/comarkPass onmax() to the renderer.
The deploy waits while more than 12 pigeons sit on the roof.
Pineapple pizza needs two thirds of the Friday vote.
:include{src="./partials/install.md" code}
---
note: The frontmatter of a partial is dropped.
---
Install the plugin with Comark, ideally before standup:
```sh
pnpm add comark comark-include
```这句话是粗体。下一句不是。
이 문장은 굵게.다음 문장은 아닙니다.
Pond census tests1 passed1 failed1 skipped4.2 s
AssertionError: expected 3 to be 4
at pond.test.ts:12:20Moving the pigeon map to a worker cut the main bundle 412 kB → 388 kB ↓ 5.8% and the time to first sighting 2.4 s → 1.9 s ↓ 20.8% on a mid-range phone
Open the filters.
Narrow the window.
optionsobjectWhat the plugin takes.
iconResolverIconscreateIcons() in comark-icon.downloadbooleanDefault: truelevel: the heading level that starts each step.start: the number of the first step.:include{src="./partials/ducks.ts" region="estimate"}
const bugs = Math.round(ducks * 0.94);
return bugs;Failing
upload.test.ts times out on files over 10 MB.Passing
Every other test passes.titlestringrequiredcolorstringDefault: primaryPress ⌘ K to search. Water is H2O.
<script> after this sentence. GitHub drops it, and so does html().
The pigeon map counts each plaza in a worker, so the page scrolls while it counts.
| Metric | Before | After | Change |
|---|---|---|---|
| JavaScript | 412 kB | 388 kB | ↓ 5.8% |
| Largest paint | 2.4 s | 1.9 s | ↓ 20.8% |
Pigeon map tests3 passed1.8 s
The worker starts on loadon the first scrollMost visitors never scroll the map.
| Mallard | 14 | North |
| Pigeon | 212 | Plaza |
| Heron | 2 | North |
ABC
The deploy waits when more than twelvefifteen pigeons sit on the roof.
Count them afterbefore the stand-up, from the east windowThe west one faces the parking lot..
Pizza is on FridayPineapple still needs a vote..
Point at the step people ask about, not the top of the page.
Internal teams only.
Press Ctrl + Shift + K to open the pigeon counter, and Esc to close it.
On a Mac, it's Cmd + Option + K. Page through the log with Page Up and Page Down.
Pizza is ordered on Fridays[1].
Pineapple needs a vote, and the vote needs two thirds.
The order goes in at noon:
pnpm exec spurious correlate --x "rubber ducks bought" --y "bugs fixed" --office berlin --moon-phase waxing --min-r 0.9 > ducks.csv| Metric | Before | After | Change |
|---|---|---|---|
| JavaScript | 412 kB | 388 kB | ↓ 5.8% |
| Largest paint | 2.4 s | 1.9 s | ↓ 20.8% |
| Requests | 38 | 41 | ↑ 7.9% |
この文は太字。次の文は普通です。
会議は「金曜日」に移りました。
鳩の数を数える。もう数えません。
The loft moves to the roof in spring.
| function close() { | ||
| dialog.close(); | ||
| history.back(); | ||
| if (opened) history.back(); | ||
| } |
123456box frames an element.circle rings a small detail.underline marks text.highlight washes over an area.cross strikes something out.check approves and sits beside its target.import { defineConfig } from "vite";
6 collapsed lines
import vue from "@vitejs/plugin-vue";
import icons from "unplugin-icons/vite";
import components from "unplugin-vue-components/vite";
import inspect from "vite-plugin-inspect";
import checker from "vite-plugin-checker";
import { comark } from "./comark";
export default defineConfig({
plugins: [comark(), vue()],
});
The button moves from #3b82f6 to oklch(0.55 0.2 264), and its hover from rgb(37 99 235) to hsl(226 71% 40%).
Built with Vue and Vite, deployed to Cloudflare.
posva maintains
Vue Router, and
danielroe leads
Nuxt.
Evan posts on Bluesky.
Note
Drafts save every few seconds, so closing the tab keeps your work.
Tip
Press ⌘K to search every document.
Important
Releases ship from main only.
Warning
Changing the slug breaks links people already shared.
Caution
Deleting a folder deletes every document in it.
function close() {
dialog.close();
history.back();
if (opened) history.back();
}Pigeon map tests3 passed1.8 s
Hold Shift and press Duck Button to page the rubber duck.
Deploy with Ctrl + Alt + 🍕 on Fridays only.
pnpm add comark @comark/html comark-codepnpm add comark-tabsnpm install comark-tabsyarn add comark-tabsThe palette page now shows every token of the pigeon tracker's theme.
| Benchmark | Before (ms) | After (ms) |
|---|---|---|
| Parse the duck census | 1,204 | 980 |
| Render the pond map | 88 | 91 |
| Count the pigeons | 412 | 388 |
Merge the pull request once its checks pass.
| export const MAX_FILTERS = 3; |

:include{src="./partials/ducks.ts" lines="8:10"}
export function ducksNeeded(bugs: number) {
return Math.ceil(bugs / 0.94);
}The number of pigeons on the office roof at 9:00.
Above 12, the deploy waits. Below 12, it ships.
Duck tests moved to Vitest: Migrated85%
Thanks onmax: this fixes #51, and onmax/comark-ui#73 builds on it.
and , as Nuxt UI writes it, draw the same icon.
ABC1$ pnpm build Bundling [==========] done dist/pigeons.js 12.4 kB dist/style.css 3.1 kB ✓ built in 412ms
function close() {
dialog.close();
history.back();
if (opened) history.back();
}Before
One long page of options.
After
Each option under its own heading.
Open the preview on your phone:
The office roof holds about 103 pigeons, and the 1st count of the day is at 9:00.


Decision
Keep the existing endpoint. Clients pin its response shape.
The deploy waits when the roof holds more than 12 pigeons[1], unless it's a hotfix[2].
npm install -D comark-tabspnpm add -D comark-tabsyarn add --dev comark-tabsbun add --dev comark-tabsThe flower opens once the page loads, as it did before the change:
ABCRUN v3.2.4 /pigeon-tracker ✓ test/flock.test.ts (6 tests) ✓ test/routes.test.ts (4 tests) ✗ test/homing.test.ts (3 tests | 1 failed) Tests 1 failed | 12 passed
A promise callback never runs in the middle of other code. It waits in the microtask queue[1], and the browser empties that queue at a fixed point in each turn of the event loop.
The current script, or one event handler, runs to its end.
Every queued promise callback runs, and so do the ones they queue[1].
Only now can the browser render, then take the next task[2].
Tip
A long chain of microtasks keeps the page from painting just like one long task does.
Which line lets the browser paint before the rest of the work runs?
await Promise.resolve()queueMicrotask(() => next())await new Promise((r) => setTimeout(r))When does the browser run the microtask queue?
Sources
dist/pigeons.js 12.4 kB dist/style.css 3.1 kB ✓ built in 412ms
node:sqlite caches the icons between builds, and it shipped in Node 22.5.| Attempt | Wait |
|---|---|
| 1 | 1 s |
| 2 | 4 s |
| 3 | Until the pigeons leave |
<details>, and type="single" gives them one name, so opening one closes the others.The alert sound the reviewer asked for:
The retro moved to Thursday[1], and the rubber duck[2] runs it.
pnpm install.main.What should the next lesson cover?
:include{src="./partials/install.md"}
Install the plugin with Comark, ideally before standup:
pnpm add comark comark-includeWhich callback runs first once the current script ends?
setTimeout(fn, 0) callbackthen callbackrequestAnimationFrame callbackDecision
Moon phase and vibes collapse into +2 filters, so the toolbar stays on one row at 640px.
| const visible = filters; | ||
| const visible = filters.slice(0, 3); | ||
| const hidden = filters.length - visible.length; |
| Width | Before | After |
|---|---|---|
| 640px | 105px | 56px |
| 1024px | 56px | 56px |
Read the incident report before the review, then come back to the checklist.
Why does a promise callback run before a zero-delay timer?
| Check | Time | Result |
|---|---|---|
| Unit tests | 1.9 s | 412 passed |
| E2E | 48 s | 3 slow |
| Type check | 6.2 s | 2 errors |
pnpm add comark-stepsPass steps() to the parser and components to the renderer.
Wrap the headings of a tutorial in ::steps.
Breaking change in v2
render(markdown) is gone. Create a renderer once instead:
plugins and componentscomponents by tag nameconst render = createHtmlRenderer({ plugins, components });pnpm add comark-tabsnpm install comark-tabsyarn add comark-tabsAt 640px, moon phase and vibes push the filters onto a second row.
1With moon phase and vibes behind +2 filters, everything fits on one row.
1
Use useFetch instead. v4 stable
Your agent writes the review in Markdown and your team reads the page. When a block is wrong, the plugins say how to fix it.
> [!TIP] Decision> Moon and Vibes move into **+2 filters**.> One row again. Fixes #51, as @onmax asked.::screenshot{src="/after.png" alt="Filter toolbar on one row"}:::annotation{x=68 y=33.1 w=13.3 h=16 mark="highlight"}Moon and Vibes are in here now.::::::annotation{x=92.582.5 y=33.1 w=10.9 h=16 mark="check" color="green"}**Apply** fits on the first row.:::::The numbers didn't move:| Pair | Before | After || --- | --- | --- || Pizzas vs. merged PRs | 0.95 | 0.95 || Rubber ducks vs. bugs fixed | 0.97 | 0.97 || Drizzle vs. standup minutes | 0.84 | 0.84 |::diff{label="Suggested change"}```css [toolbar.css].filters { flex-wrap: wrap; }``````css.filters { flex-wrap: nowrap; }```::No problems
12The numbers didn't move:
| Pair | Before | After |
|---|---|---|
| Pizzas vs. merged PRs | 0.95 | 0.95 |
| Rubber ducks vs. bugs fixed | 0.97 | 0.97 |
| Drizzle vs. standup minutes | 0.84 | 0.84 |
| .filters { flex-wrap: |
| .filters { flex-wrap: nowrap; } |
Drop renders these documents as a page your team comments on, and connects to Claude Code, Codex, or any MCP agent.