Data display

Kanban board

A Kanban board that a keyboard can actually use.

Client componentSource

Import

import { KanbanBoard } from '@the_viveksingh/vivek-ui'

A sprint board you can drive from the keyboard

HTML5 drag-and-drop has no keyboard equivalent at all, which is why nearly every Kanban board is mouse-only. This has two complete input paths: dragging, and a pick-up / move / drop model announced through a live region.

Drag with a mouse, or Tab to a card and press Enter to pick it up — then arrow keys to move, Enter to drop, Escape to cancel. Every move is announced to a screen reader.

Backlog2
  • Audit the colour paletteP3

    Check every token pair for contrast.

    Press Enter or Space to pick this card up.
  • Write the migration noteP2
    Press Enter or Space to pick this card up.
In progress1 / 2
  • Ship the Kanban boardP1

    Keyboard path is the hard part.

    Press Enter or Space to pick this card up.
In review1
  • FileTree keyboard modelP1
    Press Enter or Space to pick this card up.
Done1
  • VirtualList
    Press Enter or Space to pick this card up.
const [columns, setColumns] = useState(initial)

<KanbanBoard
  columns={columns}
  label="Sprint board"
  // Nothing is mutated for you - the board reports the intended move.
  onMove={({ cardId, fromColumnId, toColumnId, toIndex }) =>
    setColumns((current) => applyMove(current, cardId, fromColumnId, toColumnId, toIndex))
  }
/>

// Columns carry an optional work-in-progress limit, which blocks drops when reached:
const initial = [
  { id: 'backlog', title: 'Backlog', cards: [...] },
  { id: 'doing', title: 'In progress', cards: [...], limit: 2 },
  { id: 'done', title: 'Done', cards: [] },
]

Props

Generated from the package's own type declarations, so this table cannot drift from the code.

Props for KanbanBoard
PropTypeDefaultDescription
columns requiredreadonly KanbanColumn[]
label requiredstringRequired. A board with no accessible name is one more unlabelled region.
onMove(move: KanbanMove) => voidNothing is moved for you — apply this to your own state.
renderCard(card: KanbanCard, column: KanbanColumn) => ReactNodeRendered instead of the default card body.
classNamestring

Rendering