Kanban

Columns of cards, movable between them. Drag-and-drop is pointer-only, so every card also moves with the arrow keys — a board you can only drag excludes keyboard users from its main action.

src/components/ui/kanban.tsx

About

From the component's own source, where it was written.

Columns of cards, movable between them.

Uses the HTML drag-and-drop API rather than pointer maths. It is the API that gives a native drag image and works with the OS conventions, and reaching for a pointer-based library is how a board ends up unusable on touch.

Because drag-and-drop is pointer-only, every card also carries a keyboard path: focus it and use the arrow keys to move it between columns. A board where the only way to move work is dragging excludes anyone using a keyboard, and that is the majority of the actions on a board people use daily.

The board never reorders anything itself — onMove reports the intent and the caller owns the data. Optimistically moving a card and then having the server reject it is worse than a moment of latency.

Install

npx astralyx-ui add kanban

Usage

import { Kanban } from '@/components/ui/kanban'

<Kanban columns={columns} onMove={move} />

Composer

Change the props and the source below follows.

To do

2
  • Extract the copy button

    chore
  • Range slider ARIA review

In progress

1 / 3
  • Menubar roving focus

    a11y

Done

1
  • Drawer drag dismissal

Props
tsx
<Kanban columns={columns} onMove={move} />

Examples

Board

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

Kanban

Extends Omit<ComponentProps<'div'>, 'onDrop'>.

PropTypeDefaultDescription
columnsKanbanColumn[]required{ id, title, cards, limit? } where a card is { id, title, meta?, accent? }.
onMove(cardId: string, fromColumn: string, toColumn: string) => void—The board never reorders anything itself — moving optimistically and having the server reject it is worse than a moment of latency.
renderCard(card: KanbanCard, column: KanbanColumn) => ReactNode—Replace the card body while keeping the drag behaviour.
emptyLabelReactNode'Nothing here'Shown in an empty column.

Types

KanbanCard

Columns of cards, movable between them.

FieldTypeDescription
idstring—
titleReactNode—
meta?ReactNode—
accent?string—

KanbanColumn

FieldTypeDescription
idstring—
titlestring—
cardsKanbanCard[]—
limit?number—

Behaviour

TopicDetailDescription
keyboard← →Moves a focused card between columns.