Components
Sortable list and board
Reordering without a drag library: the pointer uses the platform's own drag protocol and the keyboard uses pick up, move, drop, narrated through a polite live region.
Examples
import { KanbanBoard, SortableList } from "@luc/ui";Sortable list
The order on the left is the array read back on the right: `onReorder` receives the complete order rather than a delta, so a caller never has to replay the move itself.
- Ingest04:00 · 1,284 bars
- Analytics04:12 · seven domains
- Materialize04:24 · serving generation
- Research pass05:02 · RP01
- Notify05:10 · Lark
Current order
ingest → analytics → materialize → research → notify
Drag the handle with a pointer, or press Space to pick up, the arrow keys to move, Space to drop and Escape to cancel.
<SortableList items={items} onReorder={setOrder} />Kanban board
A board is a two-axis sortable: Up/Down reorders inside a column and Left/Right carries the card into the neighbouring column at the same depth.
Backlog
- Volatility surface 3DProjected surface, no GPUcharts
- Chord diagramMatrix → arcscharts
Doing
- Bilingual bodiesLong-form copyshowcase
Done
- Spatial design languagetokens
<KanbanBoard columns={columns} onMove={handleMove} />Public API
Both components are controlled: they report the intent and the application decides how the model changes.
| Prop | Type | Default | Notes |
|---|---|---|---|
SortableList.items | SortableItem[] | — | List items: an id, a label and optional description and disabled. |
SortableList.onReorder | (ids: string[]) => void | — | The complete id order after the move. |
KanbanBoard.columns | KanbanColumn[] | — | Columns and cards; each column has an id, a title and its cards. |
KanbanBoard.onMove | ({ cardId, from, to, index }) => void | — | One move's intent: the card id, the source column, the destination column and the destination index. |
Dragging uses only native browser events, the keyboard uses `aria-pressed` plus a live announcement, and neither needs an extra dependency.
Usage
import { KanbanBoard } from "@luc/ui";<KanbanBoard
columns={columns}
onMove={({ cardId, from, to, index }) => move(cardId, from, to, index)}
label="Sprint"
/>Guidelines
Do
- Keep the move in the caller: `onMove` reports the card, the source column, the target column and the index, and the board re-renders from the new columns.
- Use `disabled` for a read-only plan rather than rendering a second static layout.
Don't
- Use a board for an ordered list with two states; a `SortableList` or a `DataTable` is the smaller instrument.
Accessibility
Each column keeps its title as a heading inside a named group, and every card is a focusable control, so a reader can reach a card and its column without a pointer.
Related