Kanban BoardLoading saved progress…

Kanban Board

Build a three-column Kanban board (To Do / In Progress / Done) as a single React component. Each column shows its title, a live count, and its cards. Cards are draggable with the native HTML5 drag-and-drop API, and dragging a card onto another column moves it there — the source loses it, the target gains it, and both counts update.

Task

The starter App.tsx renders the three columns with their initial cards and counts, but nothing moves yet. Wire up the drag-and-drop:

  1. Own the data. Hold a cards-by-column map (todo / doing / done arrays) in useState instead of the fixed const.
  2. Remember the drag. On a card's onDragStart, record which card and which column it came from.
  3. Accept the drop. On a column's onDragOver, call e.preventDefault() — without it the column is not a valid drop target and onDrop never fires. Add an over class to highlight it.
  4. Move the card. On the column's onDrop, remove the card from its source array and append it to the target array. Counts read from each array's length, so they update on their own.
  5. Match it from the keyboard. A focused card moves one column with Alt+ArrowLeft or Alt+ArrowRight. Restore focus to the moved card and announce Moved {card} to {column}. through the polite live region.

Examples

  • The board loads with To Do holding three cards (count 3), In Progress one (count 1), Done one (count 1).
  • Drag Set up CI from To Do onto In Progress: To Do drops to 2, In Progress rises to 2, and the card appears at the bottom of In Progress.
  • While a card hovers over a column, that column's border turns green (the over highlight); it clears when you leave or drop.
  • Focus Write specs and press Alt+ArrowRight: it appends to In Progress, focus follows it, and assistive technology hears Moved Write specs to In Progress.

Notes

  • Move between columns only. No within-column reordering — a drop always appends to the end of the target column.
  • onDragOver must preventDefault. It is the single most-missed step; skip it and drops silently do nothing.
  • Counts are derived, not stored. Read columns[id].length at render time; never keep a separate counter in sync.
  • No-op boundaries. Dropping on the source column, pressing left in To Do, or pressing right in Done changes nothing and makes no announcement.
  • Shared accessibility contract. Every card is focusable, names its card and column, the board references keyboard instructions, and successful pointer and keyboard moves use the same helper and live message.
  • Styling (dark board, columns, cards, the over and dragging states) is already in styles.css; focus on the state and the handlers.
WE’RE LISTENING

Help make UIReady better

Found a bug or have an idea? Tell us about it.

Up to 3 files · 5 MiB each · JPG, PNG, WebP, MP4, WebM, MP3, M4A or WAV