Skip to content
All builds

KansoKanban Board

What it does for you

A local-first kanban board with pointer and keyboard drag and drop, full undo, WIP limits, swimlanes and a flow chart, built on a pure reducer core with no framework.

Kanso screenshot

Screenshots

Kanso screenshot 1
Kanso screenshot 2
Kanso screenshot 3

In more detail

Kanso is a kanban board that runs from a folder in your browser. You drag cards between columns, or move them with the keyboard, and you can undo anything, including imports and deleted columns. It started life as Tack, but #14 (Map Pin Board) already had that name, so I renamed it Kanso to keep the portfolio clean.

Honest limits

101 of the 105 assertions run in plain Node against the core. The UI was checked by headless screenshots and the smoke test, not by a long session of me clicking around. I cut touch drag and drop, a board switcher, column drag reordering, bulk select, a tag manager, and virtualization for very large boards. Built and tested, not battle-hardened.

How I built it

This is the project where the whole sprint nearly broke, and where it got fixed.

Claude Code wrote the core first, kanban.js: an immutable reducer, a history stack, rank keys, filters, WIP checks, exporters, and a schema migration, with no DOM in it at all. That went fine in the morning.

Then the UI attempts started dying. The API connection was dropping long responses mid-stream ("the response stopped arriving"), and every agent that tried to write one big UI file lost its run. Our first theory was that single oversized writes were the problem. The transcripts said otherwise: it was the stream itself.

So I gave the next run a hard constraint: tiny writes, one tool call per response, many small modules. That run was the pilot for what became the tiny-writes brief. It finished the entire UI in about 15 minutes (17:55 to 18:10), as roughly twenty small ui-*.js modules that only read state, render, and dispatch.

Claude Code also tested its own work in headless Chrome. It read its own screenshots back and caught a stray "null" in the tag list and a doubled focus ring, then wrote a smoke test that drives real pointer and keyboard events through drag, undo, keyboard move, the card dialog, and search.

What I learned

  • Undo gets cheap when state is immutable. History is just a list of previous state references, and a coalesce key on an action merges a whole keyboard move, or a burst of typing, into one undo step.
  • String rank keys beat integer positions. A drop computes a key between two neighbors, so one card changes instead of a whole column, and drag, keyboard move, import, and undo all share one code path.
  • A dialog that refuses to overwrite a focused input has to fill its inputs before focus lands, or the title opens blank. The smoke test caught that one.
  • Headless Chrome under a virtual time budget does not reliably fire requestAnimationFrame. My aria-live announcements were frame based and never ran. A short timer fixed it.
  • Size the unit of work to what the pipe can carry. Twenty files under 100 lines each survived a connection that killed every single 500-line write.

What carried forward

The tiny-writes rule from this pilot finished the other stalled builds: #32, #34, #35, and #37 all credit small modules and short writes in their own case studies. The shape underneath came from #9: a pure core imported by both the page and verify.mjs, so the shipped code is the tested code.

If I took it further

I would render with keyed DOM patches instead of rebuilding the board on every change, since a lot of code goes into restoring focus and scroll. Then touch drag and the board switcher.

#031 Built with Claude Code September 20, 2026 105 automated checks

Want one like this?