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
| Prop | Type | Default | Description |
|---|---|---|---|
| columns required | KanbanColumn[] | — | Array of columns with their cards. |
| onmove | (event: KanbanMoveEvent) => void | — | Called after a reorder or cross-column drop. Omit only for read-only previews. |
| onadd | (columnId: string) => void | — | When 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 | string | — | Additional 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
| Prop | Type | Default | Description |
|---|---|---|---|
| id required | string | — | Unique column identifier. |
| title required | string | — | Column header text. |
| cards required | KanbanCard[] | — | Cards in this column. |
| color | string | — | Accent color for the column header. |
KanbanCard
| Prop | Type | Default | Description |
|---|---|---|---|
| id required | string | — | Unique card identifier. |
| title required | string | — | Card title text. |
| description | string | — | Optional card description. |
| labels | { text: string; color: string }[] | — | Optional label tags with text and color. |
KanbanMoveEvent
| Prop | Type | Default | Description |
|---|---|---|---|
| card required | KanbanCard | — | The card being moved. |
| fromColumnId required | string | — | Source column ID. |
| fromIndex required | number | — | Original index in source column. |
| toColumnId required | string | — | Target column ID. |
| toIndex required | number | — | Target index in destination column. |
Usage tips
- The
onmovehandler receives the full move event — you’re responsible for updating column state immutably. - Use the
cardsnippet prop for custom card rendering (e.g. avatars, due dates, priority indicators). - Column
coloris rendered as a header accent — use brand or status colors to differentiate lanes. - Cards support
labelsfor lightweight categorization displayed as tags. DndGhostandDndIndicatorship inside the component — import bundled styles so drag visuals match other DnD surfaces.