Validate against the staging IDP; fall back to existing token endpoint.
- due:May 12
Drag-and-drop kanban board with swimlanes, tinted columns, per-column rules, and a renderer registry that hosts any card type.
Use when you need a Trello/Linear/JIRA-style board for tracking work, pipelines, or stage transitions. The renderer-registry pattern lets a single column mix the lightweight built-in `kanban-card`, the `kanban-note` annotation, and any rich card from elsewhere in this registry — all as siblings in an ordered, JSON-serializable item list. CRUD affordances are opt-in via callbacks; DnD via @dnd-kit (touch + keyboard accessible).
pnpm dlx shadcn@latest init"registries": {
"@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}pnpm dlx shadcn@latest add @ilinxa/kanban-boardAdd -fixtures for dummy data:
pnpm dlx shadcn@latest add @ilinxa/kanban-board-fixturesValidate against the staging IDP; fall back to existing token endpoint.
Coordinate with the auth team on the new session shape before merging.
Tighten spacing on the notifications panel.
Decide between LRU and ARC for the new session cache layer.
Posted in #infra-incidents — link in the action items list.
Reach for KanbanBoard when you need a column-based board with drag-and-drop reordering, optional swimlanes, optional CRUD, and the flexibility to host any card from this registry (or your own custom components) as first-class items in any column.
items[] is an array of { id, rendererId, data, swimlaneId?, locked? } records. The board never holds JSX in its data layer.kanbanCardRenderer, kanbanNoteRenderer); register more via the renderers prop. Each declares an id and a render(data, ctx).onItemCreateand an inline "+ Add" row appears under each column. Same pattern for edit, delete, and column CRUD callbacks.import { KanbanBoard } from "@/components/kanban-board";
import { kanbanCardRenderer } from "@/components/kanban-board/parts/kanban-card";
import { kanbanNoteRenderer } from "@/components/kanban-board/parts/kanban-note";
export function Example() {
return (
<KanbanBoard
renderers={[kanbanCardRenderer, kanbanNoteRenderer]}
defaultData={{
columns: [
{
id: "todo",
title: "To do",
items: [
{ id: "c1", rendererId: "kanban-card", data: { title: "Wire auth flow" } },
{ id: "n1", rendererId: "kanban-note", data: { title: "Reminder", body: "Coordinate with infra." } },
],
},
{ id: "doing", title: "In progress", color: "lime", items: [] },
{ id: "done", title: "Done", color: "emerald", items: [], allowReorder: false },
],
}}
/>
);
}column.allowReorder: false — items cannot reorder within this column.column.allowIncoming: false — items cannot be dropped into this column from elsewhere.column.allowOutgoing: false — items cannot leave this column.column.acceptsRendererIds: [...] — only host the listed renderer kinds.item.locked: true — pin an individual item; it cannot be dragged anywhere.readOnly at the board level kills all DnD and CRUD affordances; items remain clickable.Any sibling registry component can be plugged in as a third renderer with all of its features intact. The demo wires <CardTree> from @ilinxa/card-tree — same pattern works for any rich card you author. Set dragHandle: "header" for renderers that own internal pointer interactions so the kanban grip appears on top and the body stays interactive:
import { CardTree, type CardTreeJsonNode } from "@ilinxa/card-tree";
function makeCardTreeRenderer(
onChange: (id: string, next: CardTreeJsonNode) => void,
): KanbanCardRenderer<CardTreeJsonNode> {
return {
id: "card-tree",
label: "Card tree",
dragHandle: "header", // ← thin grip strip; body stays interactive
render: (data, ctx) => (
<CardTree
key={ctx.itemId}
defaultValue={data}
editable
onChange={(tree) => onChange(ctx.itemId, tree)}
/>
),
};
}
<KanbanBoard
renderers={[kanbanCardRenderer, kanbanNoteRenderer, makeCardTreeRenderer(updateData)]}
defaultData={{ /* items reference rendererId: "kanban-card" | "kanban-note" | "card-tree" */ }}
/>dragHandle modes: "shell" (default) makes the whole card the drag activator — right for plain content cards. "header" renders a small grip strip on top and leaves the body fully interactive — right for renderers with click-to-edit fields, embedded inputs, or their own internal DnD.
Pre-built renderer — todo items: @ilinxa/task-card exports a ready-to-use taskCardKanbanRenderer (typed as KanbanCardRenderer<TaskItem>, dragHandle: "header") — no factory wrapper needed. Drop it directly into renderers={[...]} and give each item rendererId: "task-card" with a TaskItem-shaped data payload. See the task-card detail page for the live kanban demo + the full code recipe.
Tab cycles focus through items and column headers.Space on a focused item lifts it (DnD mode); arrow keys move; Space drops; Escape cancels.Enter on a focused item fires onItemClick.