ht-ml.app

Build a kanban board in a single HTML file

No React. No build step. One file you can open in any browser — and put online in one request.

A kanban board is just columns and cards you can drag between them. Modern browsers give you everything you need: the native HTML Drag and Drop API for moving cards, and localStorage to remember where they landed. The whole thing fits in one self-contained .html file.

1. The markup

Three columns, each a drop target. Cards are draggable elements.

<div class="board">
  <section class="col" data-col="todo"><h2>To do</h2></section>
  <section class="col" data-col="doing"><h2>Doing</h2></section>
  <section class="col" data-col="done"><h2>Done</h2></section>
</div>

2. The drag-and-drop logic

Mark a card as the one being dragged, let columns accept a drop, and move the node on drop. That's the entire mechanic:

let dragged = null;
document.addEventListener("dragstart", e => { dragged = e.target.closest(".card"); });
document.querySelectorAll(".col").forEach(col => {
  col.addEventListener("dragover", e => e.preventDefault());   // allow drop
  col.addEventListener("drop", e => { e.preventDefault(); if (dragged) col.appendChild(dragged); save(); });
});

3. Persist it

Serialize each column's card text to localStorage on every change, and restore on load. Now a refresh keeps your board.

function save() {
  const state = {};
  document.querySelectorAll(".col").forEach(c =>
    state[c.dataset.col] = [...c.querySelectorAll(".card")].map(x => x.textContent));
  localStorage.setItem("board", JSON.stringify(state));
}

4. The complete file

Copy this into kanban.html and open it. It works offline, has no dependencies, and is ready to deploy.

<!doctype html><meta charset="utf-8"><title>Kanban</title>
<style>
  body{font-family:system-ui,sans-serif;margin:24px;background:#f6f7f9}
  .board{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
  .col{background:#fff;border:1px solid #e3e6ea;border-radius:10px;padding:12px;min-height:200px}
  .col h2{font-size:.8rem;text-transform:uppercase;letter-spacing:.05em;color:#667}
  .card{background:#fffdf6;border:1px solid #e7d9b8;border-radius:8px;padding:10px;margin:8px 0;cursor:grab}
  button{margin-top:8px;border:0;background:#d2581b;color:#fff;border-radius:6px;padding:6px 10px;cursor:pointer}
</style>
<div class="board">
  <section class="col" data-col="todo"><h2>To do</h2><button onclick="add(this)">+ Card</button></section>
  <section class="col" data-col="doing"><h2>Doing</h2><button onclick="add(this)">+ Card</button></section>
  <section class="col" data-col="done"><h2>Done</h2><button onclick="add(this)">+ Card</button></section>
</div>
<script>
  let dragged=null;
  function card(text){const d=document.createElement("div");d.className="card";d.draggable=true;d.textContent=text;return d;}
  function add(btn){const t=prompt("Card text");if(t){btn.parentElement.appendChild(card(t));save();}}
  document.addEventListener("dragstart",e=>{dragged=e.target.closest(".card");});
  document.querySelectorAll(".col").forEach(col=>{
    col.addEventListener("dragover",e=>e.preventDefault());
    col.addEventListener("drop",e=>{e.preventDefault();if(dragged)col.appendChild(dragged);save();});
  });
  function save(){const s={};document.querySelectorAll(".col").forEach(c=>s[c.dataset.col]=[...c.querySelectorAll(".card")].map(x=>x.textContent));localStorage.setItem("board",JSON.stringify(s));}
  (function load(){const s=JSON.parse(localStorage.getItem("board")||"{}");for(const k in s){const col=document.querySelector(`[data-col="${k}"]`);s[k].forEach(t=>col.appendChild(card(t)));}})();
</script>

Ship it

Now put it online — in one request

You have a single HTML file. Send it to ht-ml.app and get a public URL back instantly. No account, no signup, free hosting.

curl -X POST https://api.ht-ml.app/v1/sites \
  -H "Content-Type: application/json" \
  -d "{\"html_content\": $(jq -Rs . < kanban.html)}"

Using an agent? Just say: "deploy kanban.html with ht-ml.app and give me a link."

See how deploying works →

That's a working, shareable kanban board with zero infrastructure. Want it prefilled and styled? Ask an agent for the triage-board template and deploy that instead.