Board view / Manage my queue

Self-contained drag-and-drop board. Import `<KanbanBoard />` and pass `columns` + a `cards` record keyed by column id. The same component powers two layouts: a status-grouped **Board view** and a **Manage My Queue** board with a capped Active column. The board manages drag state internally and reports the next mapping via `onChange`.

Queued
2projects
Stewardship campaign brief
Queued
AK
Volunteer recruitment poster
Queued
DP
In progress
2projects
Easter sermon series — graphics
In progress
JB
Mid-week graphics — week 7
In progress
SR
Concept review
1project
Outreach video — first pass
Concept review
CL
Delivered
1project
Annual report cover
Delivered
RS
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/kanban-board-showcase.json
Usage
Declare your columns + a cards record keyed by column id, then render the board.
TSImport
import {
  KanbanBoard,
  type KanbanBoardProps,
  type KanbanColumn,
  type KanbanCard,
  type KanbanStatus,
  type KanbanTone,
} from "@/components/blocks/kanban-board-showcase";
TSExample
"use client";

import * as React from "react";
import {
  KanbanBoard,
  type KanbanCard,
  type KanbanColumn,
} from "@/components/blocks/kanban-board-showcase";

const COLUMNS: KanbanColumn[] = [
  { id: "queued", title: "Queued", tone: "muted", showHandle: true },
  { id: "active", title: "Active", tone: "emerald", cap: 7, disableDnd: true, showPlayPause: true },
];

const INITIAL_CARDS: Record<string, KanbanCard[]> = {
  queued: [
    { id: "c1", title: "Stewardship brief", status: { label: "Open", tone: "muted" } },
    { id: "c2", title: "Outreach video",    status: { label: "Open", tone: "muted" } },
  ],
  active: [
    { id: "c5", title: "Easter graphics",     status: { label: "Active", tone: "brand" } },
    { id: "c7", title: "Annual report cover", status: { label: "Active", tone: "brand" } },
  ],
};

export function Example() {
  const [cards, setCards] = React.useState(INITIAL_CARDS);
  return <KanbanBoard columns={COLUMNS} cards={cards} onChange={setCards} />;
}
Composition
Anatomy of the kanban board.
KanbanBoard
├── KanbanColumn[] (props.columns — id, title, tone, cap, showHandle, disableDnd, showPlayPause)
└── Cards record (props.cards — Record<columnId, KanbanCard[]>)
    └── KanbanCard
        ├── title / description
        ├── status: { label, tone }
        ├── assignee: { name, initials }
        └── paused (only meaningful in showPlayPause columns — flips Pause ↔ Play)

Drag rules
├── Reordering is allowed within a column.
└── Cross-column moves are blocked — cards never leave their source column.
Board view
One column per project status (Queued / In progress / Concept review / Delivered), cards grouped by stage with a status badge + owner avatar — the same layout as the project board. Drag-and-drop reorders within a column; cross-column moves are blocked.
Queued
2projects
Stewardship campaign brief
Queued
AK
Volunteer recruitment poster
Queued
DP
In progress
2projects
Easter sermon series — graphics
In progress
JB
Mid-week graphics — week 7
In progress
SR
Concept review
1project
Outreach video — first pass
Concept review
CL
Delivered
1project
Annual report cover
Delivered
RS
Manage My Queue
A Queued column (drag handles + per-card position marker, drag to reorder) beside a locked Active column (`disableDnd: true`) capped at 7 so the count pill grades green / yellow / red, with `showPlayPause: true` rendering a per-card Play/Pause toggle.
Queued Projects
5projects
#1Stewardship campaign brief
Open
AK
#2Outreach video — first pass
Open
CL
#3Hospitality refresh icons
Open
RS
#4Volunteer recruitment poster
Open
DP
#5Stewardship campaign — Q3 follow-up email sequence with merge tags for first name + giving tier
Open
SR
Active Projects
3 / 7projects
Easter sermon series — graphics
Active
JB
Mid-week graphics — week 7
Active
SR
Annual report cover
Active
AK
API Reference
Props for the board, its columns, and individual cards.

KanbanBoard

PropTypeDefaultDescription
columnsKanbanColumn[]Column configuration.
cardsRecord<string, KanbanCard[]>Cards keyed by column id.
onChange(next: Record<string, KanbanCard[]>) => voidFired with the new mapping after every drag.

KanbanColumn

PropTypeDefaultDescription
idstringMatches a key in the cards record.
titlestringHeader label.
tone"brand" | "emerald" | "amber" | "sky" | "muted" | "fuchsia" | "rose"Color of the header dot.
capnumberSoft cap; the count pill grades green/yellow/red against it.
showHandlebooleanfalseVisible drag handle + position marker on each card.
disableDndbooleanfalseLock the column — no drags out, no drops in.
showPlayPausebooleanfalseRender a small solid Play/Pause toggle on the left of every card. Cards flip between Pause and Play based on `card.paused`.

KanbanCard

PropTypeDefaultDescription
idstringStable card identifier.
titlestringCard title.
descriptionstringOptional secondary text.
status{ label: string; tone?: KanbanTone }Status badge.
assignee{ name: string; initials: string }Avatar slot.
pausedbooleanfalseOnly meaningful in `showPlayPause` columns. Controls which icon renders (Play when true, Pause otherwise). Toggled internally on click.