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) => 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

PropTypeDefaultDescription
id requiredstring-Unique column identifier.
title requiredstring-Column header text.
cards requiredKanbanCard[]-Cards in this column.
color string-Accent color for the column header.

KanbanCard

PropTypeDefaultDescription
id requiredstring-Unique card identifier.
title requiredstring-Card title text.
description string-Optional card description.
labels { text: string; color: string }[]-Optional label tags with text and color.

KanbanMoveEvent

PropTypeDefaultDescription
card requiredKanbanCard-The card being moved.
fromColumnId requiredstring-Source column ID.
fromIndex requirednumber-Original index in source column.
toColumnId requiredstring-Target column ID.
toIndex requirednumber-Target 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.