Phoundry UI

KanbanBoard

Drag-and-drop kanban board with columns, cards, labels, and custom card rendering.

import { KanbanBoard } from 'phoundry-ui';

Basic Board

To Do 3
Design landing page
Create mockups for the new landing page
design
Set up CI/CD
devops
Write API docs
Document REST endpoints
docs
In Progress 2
Implement auth flow
OAuth2 + session management
backend
Build component library
frontend
Done 1
Project setup
devops
Show code
let columns = $state([
  {
    id: 'todo', title: 'To Do', color: '#6366f1',
    cards: [
      { id: 'c1', title: 'Design landing page', labels: ['design'] },
      { id: 'c2', title: 'Set up CI/CD', labels: ['devops'] },
    ],
  },
  {
    id: 'progress', title: 'In Progress', color: '#f59e0b',
    cards: [
      { id: 'c3', title: 'Implement auth flow', labels: ['backend'] },
    ],
  },
  {
    id: 'done', title: 'Done', color: '#22c55e',
    cards: [],
  },
]);

function handleMove(event) {
  const newColumns = columns.map((col) => ({
    ...col,
    cards: col.cards.filter((c) => c.id !== event.card.id),
  }));
  const target = newColumns.find((c) => c.id === event.toColumnId);
  target.cards.splice(event.toIndex, 0, event.card);
  columns = newColumns;
}

<KanbanBoard {columns} onmove={handleMove} />

Add-card footer & custom header

onadd is separate from the optional header snippet — this demo uses both for illustration.

To Do 3
Design landing page
Create mockups for the new landing page
design
Set up CI/CD
devops
Write API docs
Document REST endpoints
docs
In Progress 2
Implement auth flow
OAuth2 + session management
backend
Build component library
frontend
Done 1
Project setup
devops
Show code
{#snippet columnHeader(column)}
  <div class="flex items-center justify-between border-b px-3 py-2">
    <span class="text-xs font-semibold">{column.title}</span>
    <Button size="xs" variant="ghost" icon="carbon:add" iconOnly title="Add" onclick={() => onadd(column.id)} />
  </div>
{/snippet}

<KanbanBoard {columns} onmove={handleMove} onadd={(id) => openComposer(id)} {columnHeader} />

Props

PropTypeDefaultDescription
columns requiredKanbanColumn[]Array of columns with their cards.
onmove (event: KanbanMoveEvent) => voidCalled after a reorder or cross-column drop. Omit only for read-only previews.
onadd (columnId: string) => voidWhen set, each column shows an “Add card” footer that passes its column id.
columnHeader Snippet<[KanbanColumn]>Replace the default header row (dot, title, count).
class stringAdditional CSS classes on the horizontal scroller.
card Snippet<[KanbanCard, columnId: string]>Custom card body; you still get drag handles via the outer `dndItem` wrapper.

KanbanColumn

PropTypeDefaultDescription
id requiredstringUnique column identifier.
title requiredstringColumn header text.
cards requiredKanbanCard[]Cards in this column.
color stringAccent color for the column header.

KanbanCard

PropTypeDefaultDescription
id requiredstringUnique card identifier.
title requiredstringCard title text.
description stringOptional card description.
labels { text: string; color: string }[]Optional label tags with text and color.

KanbanMoveEvent

PropTypeDefaultDescription
card requiredKanbanCardThe card being moved.
fromColumnId requiredstringSource column ID.
fromIndex requirednumberOriginal index in source column.
toColumnId requiredstringTarget column ID.
toIndex requirednumberTarget index in destination column.

Usage tips

  • The onmove handler receives the full move event — you’re responsible for updating column state immutably.
  • Use the card snippet prop for custom card rendering (e.g. avatars, due dates, priority indicators).
  • Column color is rendered as a header accent — use brand or status colors to differentiate lanes.
  • Cards support labels for lightweight categorization displayed as tags.
  • DndGhost and DndIndicator ship inside the component — import bundled styles so drag visuals match other DnD surfaces.