KanbanBoard

App

Trello / Linear-style kanban board. M1 ships HTML5-native drag and drop between columns with an inline drop-position indicator (thin line between cards) and full optimistic-UI rewind on rejected onCardMove. Use the canMove hook to gate transitions client-side, or throw from onCardMove to revert. Future milestones: column reorder + collapse + WIP limits (M2), swimlanes + filters + search (M3), inline edit + bulk select + card detail panel (M4), keyboard nav + ARIA announcements (M5), virtualization + auto-archive + dependencies (M6). Pixel-identical EJS sibling at modules/app/KanbanBoard/KanbanBoard.ejs.

Three columns (basic)

Preview

To Do

3
  • Audit dark-mode tokens

    mediumdesign
  • Wire i18n for /account

    lowi18n
  • Fix flaky e2e on Firefox

    urgentbug

In Progress

2
  • Refactor Quill toolbar

    mediumJD
  • Document KanbanBoard API

    JSAL

Done

1
  • Ship release notes

    highdocs
Code
const columns = [
  { id: 'todo',  title: 'To Do' },
  { id: 'doing', title: 'In Progress' },
  { id: 'done',  title: 'Done' },
];
const [cards, setCards] = useState(initialCards);
<KanbanBoard
  columns={columns}
  cards={cards}
  onCardMove={(card, from, to) =>
    setCards((prev) =>
      prev.map((c) => (c.id === card.id ? { ...c, columnId: to } : c)),
    )
  }
/>

canMove validation (no exit from Done)

Preview

To Do

3
  • Audit dark-mode tokens

    mediumdesign
  • Wire i18n for /account

    lowi18n
  • Fix flaky e2e on Firefox

    urgentbug

In Progress

2
  • Refactor Quill toolbar

    mediumJD
  • Document KanbanBoard API

    JSAL

Done

1
  • Ship release notes

    highdocs
Code
<KanbanBoard
  columns={columns}
  cards={cards}
  canMove={(card, from, to) => from !== 'done' || to === 'done'}
  onCardMove={persist}
/>

Async onCardMove with rollback

Preview

To Do

3
  • Audit dark-mode tokens

    mediumdesign
  • Wire i18n for /account

    lowi18n
  • Fix flaky e2e on Firefox

    urgentbug

In Progress

2
  • Refactor Quill toolbar

    mediumJD
  • Document KanbanBoard API

    JSAL

Done

1
  • Ship release notes

    highdocs
Code
<KanbanBoard
  columns={columns}
  cards={cards}
  onCardMove={async (card, from, to) => {
    const res = await fetch('/api/move', { method: 'POST', body: JSON.stringify({ card, to }) });
    // Throw to revert the optimistic update.
    if (!res.ok) throw new Error('Server rejected');
    setCards(next);
  }}
/>
Sourcemodules/app/KanbanBoard/index.tsx