# Kanban A board of columns of cards you reorder and move by pointer, touch or keyboard, with announcements and a one-column phone layout. ```ehtml Example import { Kanban, KanbanBoard, KanbanColumn, KanbanItem, KanbanItemMove, KanbanNav, KanbanNavItem, } from "@elements/ui/kanban"; c} value={id}>{TITLES[id]} c} value={id} title={TITLES[id]} count={state.columns[id].length}> t.id} value={task.id}> {task.title} ``` ## Parts | Part | What it is | |---|---| | `Kanban` | The root. Owns value (two-way) and every drag; renders the drag overlay in the top layer. | | `KanbanBoard` | The row of columns (a stack when vertical). Scrolls on its main axis; snaps one column at a time on a phone. | | `KanbanColumn` | One column: a header from title and count, the card list (scrolls on its own), and header, actions and footer slots. | | `KanbanColumnHandle` | A grip button that drags its column. Columns reorder only with a handle or asHandle. | | `KanbanItem` | One card. It drags from a KanbanItemHandle, or as a whole card with asHandle; with neither it stays put. | | `KanbanItemHandle` | A grip button that drags its card; the rest of the card stays clickable and selectable. | | `KanbanItemMove` | Previous and next column buttons, the tap control every draggable needs on a phone. Shown in the compact layout by default. | | `KanbanNav` | A row of column buttons above the board on a phone: each scrolls to its column, and the one in view is current. | | `KanbanNavItem` | One column's button in KanbanNav. | | `.kanban-drop / .kanban-board-drop` | The placeholder that marks the slot while a card or column is over it. | | `.kanban-overlay` | The dragged copy, in the top layer, so a scrolling column never clips it. | ## Attributes ### Kanban | Attribute | Type | Default | Description | |---|---|---|---| | `class` | `string` | `""` | Extra classes on the part's root. | ### Kanban | Attribute | Type | Default | Description | |---|---|---|---| | `value` | `Record` (two-way) | `{}` | Column id to its items; key order is column order. Written once per move, never during a drag. | | `getItemValue` | `(item: any) => string \| number` | `item.id, else the item` | The id of an item. Must match its KanbanItem value. | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Columns side by side, or stacked. | | `label` | `string` | `""` | The board's accessible name. | | `dragging` | `{ kind: "item" \| "column"; id: string } \| null` (two-way) | `null` | What is being dragged. Bind it to render a custom overlay. | | `disabled` | `boolean` | `false` | Nothing drags and move buttons are disabled. | | `autoScroll` | `boolean` | `true` | Scroll the board, the column and the page when a dragged card nears their edges. | | `restrictTo` | `"none" \| "vertical" \| "horizontal" \| "board" \| "window"` | `"none"` | Lock the dragged copy to an axis, or keep it inside the board or the window. | | `keyboardCodes` | `{ start?: string[]; cancel?: string[]; end?: string[] }` | `Space, Enter; Escape; Space, Enter, Tab` | The KeyboardEvent.code values that pick up, cancel and drop. Change instructions to name them. | | `flatCursor` | `boolean` | `false` | Default cursor instead of grab and grabbing. | | `announcements` | `{ start?, over?, end?, cancel? }` | `{}` | Replace any screen reader message. Each gets { kind, label, column, position, total }; positions start at 1. | | `instructions` | `string` | `pick up, move, drop, cancel` | Screen reader instructions wired to every activator with aria-describedby. | | `roleDescription` | `string` | `"sortable"` | aria-roledescription on every activator; translate it with instructions and announcements. | | `onValueChange` | `(value) => void` | `() => {}` | After the board wrote a new value. | | `onMove` | `(e: KanbanMoveEvent) => void` | `() => {}` | Before a move is written: kind, id, from, to, value. e.preventDefault() keeps value (a limit, or a server that decides). | | `onDragStart` | `(e: KanbanDragEvent) => void` | `() => {}` | A card or column was picked up. | | `onDragOver` | `(e: KanbanDragEvent) => void` | `() => {}` | The slot under the drag changed (e.over). | | `onDragMove` | `(e: KanbanDragEvent) => void` | `() => {}` | Every pointer move and keyboard step while dragging. | | `onDragEnd` | `(e: KanbanDragEvent) => void` | `() => {}` | Dropped, moved or not. | | `onDragCancel` | `(e: KanbanDragEvent) => void` | `() => {}` | Escape, a lost pointer, blur, or a drop onMove refused cancelled the drag. | ### Kanban, KanbanColumn | Attribute | Type | Default | Description | |---|---|---|---| | `attrs` | `Record` | `{}` | Other attributes for the root or the column: id, data-testid. | ### KanbanColumn | Attribute | Type | Default | Description | |---|---|---|---| | `value` | `string` | (required) | The column's key in value. | | `title` | `string` | `""` | The header title; names the column. | | `count` | `string \| number` | `""` | A count tag beside the title. | | `label` | `string` | `""` | The column's name when it has no title. | | `disabled` | `boolean` | `false` | Cannot be dragged and takes no cards. | | `asHandle` | `boolean` | `false` | A press anywhere on the column (not on a card or a control) drags it; its grab is the keyboard button. | ### KanbanItem | Attribute | Type | Default | Description | |---|---|---|---| | `value` | `string \| number` | (required) | The item's id. | | `label` | `string` | `its text` | The name announcements use. | | `disabled` | `boolean` | `false` | Cannot be dragged or moved; others still move around it. | | `asHandle` | `boolean` | `false` | The whole card drags, even with a KanbanItemHandle inside. Without it or a handle the card does not drag. | | `attrs` | `Record` | `{}` | Other attributes for the card: id, data-testid. | ### KanbanItemHandle, KanbanColumnHandle | Attribute | Type | Default | Description | |---|---|---|---| | `label` | `string` | `"Drag card" / "Drag column"` | The handle's accessible name. | | `disabled` | `boolean` | `false` | The handle does not drag; its card or column stays enabled. | ### KanbanItemMove | Attribute | Type | Default | Description | |---|---|---|---| | `show` | `"compact" \| "always"` | `"compact"` | Show the move buttons only in the one-column layout, or always. | | `labels` | `{ to?, previous?, next?, noPrevious?, noNext? }` | `"Move to {column}", "No previous column", "No next column"` | The buttons' accessible names, for translation. {column} in to is the target column's name. | ### KanbanNav | Attribute | Type | Default | Description | |---|---|---|---| | `label` | `string` | `"Columns"` | The nav's accessible name. | | `always` | `boolean` | `false` | Show at every width, not only in the one-column layout. | ### KanbanNavItem | Attribute | Type | Default | Description | |---|---|---|---| | `value` | `string` | (required) | The column it scrolls to. | ### KanbanItemHandle, KanbanColumnHandle, KanbanNavItem | Attribute | Type | Default | Description | |---|---|---|---| | `attrs` | `Record` | `{}` | Other attributes for the button: id, data-testid, title. | ## Slots | Slot | Part | Content | |---|---|---| | `default` | `Kanban` | KanbanNav and KanbanBoard. | | `overlay` | `Kanban` | Custom drag overlay content; replaces the copy of the dragged element while it has content. | | `default` | `KanbanColumn` | The cards. | | `actions` | `KanbanColumn` | The end of the header: a handle, a menu. | | `header` | `KanbanColumn` | A header of your own, for a column without title. | | `footer` | `KanbanColumn` | Below the cards: an add button. | | `default` | `KanbanItem` | Card content. | | `icon` | `KanbanItemHandle, KanbanColumnHandle` | The grip; a grip icon by default. | ## Keyboard | Keys | Action | |---|---| | `Space / Enter` | Pick up the focused card or column; press again to drop it. | | `ArrowUp / ArrowDown` | Move the card one slot in its column. On a vertical board, past the end into the next column. Move a column on a vertical board. | | `ArrowLeft / ArrowRight` | Move the card to the previous or next column at the nearest slot (flipped right to left). Move a column. | | `Home / End` | First or last slot. | | `Escape` | Cancel; the card returns. | | `Tab` | Next card or control. While lifted, drops and keeps focus on the card. | | `ArrowLeft / ArrowRight (nav)` | Move between column buttons. | ## Data attributes | Attribute | Values | On | Description | |---|---|---|---| | `data-orientation` | `"horizontal" \| "vertical"` | root, board | | | `data-dragging` | `"item" \| "column"` | root | While a drag runs. | | `data-dragging` | present | card, column, activator | On the one being dragged. | | `data-over` | present | column | The column a card would drop into. | | `data-disabled` | present | root, column, card, handle | | | `data-kanban-activator` | present | card, handle, grab | What takes the keys that start a drag. | | `data-kanban-surface` | present | card, column | Its grab is the button; a press on it drags. | | `data-active` | present | .kanban-drop, .kanban-board-drop | While it marks the slot. | | `data-source` | `"pointer" \| "keyboard"` | overlay | | | `data-state` | `"dragging" \| "dropping"` | overlay | | | `aria-current` | `"true"` | nav item | The column in view. | ## Accessibility - The board is a group named by label; each column is a group named by its title, and its cards are a list: each card sits in a listitem. - Every activator is a button (a handle, or role="button" on an asHandle card, so every draggable has one) and a tab stop with aria-roledescription="sortable" (roleDescription) and instructions wired by aria-describedby; it reports aria-pressed="true" while lifted and aria-disabled when it cannot move. A handle's aria-controls names the card or column it drags. - No control sits inside a role="button": an asHandle card that holds controls, and an asHandle column, get a grab instead, an empty button laid over them, named for them, which takes the keys and the focus ring while presses still drag the card or column. A grip inside an asHandle card or column is inert. - Pick up, every slot change, drop and cancel are announced in a live region with positions that start at 1 and the column's name. - Focus stays on the card during a keyboard drag and follows it into its new column on drop. - Every drag has a tap equivalent: KanbanItemMove's buttons are named for the column they move to and disabled at the ends.