# Drag-and-drop boards that only a mouse can use

Source: https://easeweb.dev/learn/drag-and-drop
Topics: Drag and drop > Items can be moved between lists and boards without dragging (WCAG 2.1.1, 2.5.7, 4.1.3)
The fix: Keep the drag, and give every card a way to move that needs no drag: either a button for each column it can go to, or a Move to list with a Move button. Announce each move in a status region, and keep focus on the moved card. Keyboard dragging with the arrow keys helps keyboard users, but on its own still leaves out people who use a single pointer.
Test with: keyboard, mouse, touch, screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/draggable

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A task board has three columns: To do, Doing and Done. With a mouse, you press on a card, move it across and let go over another column, and it lands there. Press Tab and focus jumps straight past the board: the cards are not in the Tab order, and nothing on them can be pressed. On a phone, the same press starts a scroll instead of a drag. When a card does move, the board changes silently.

## Why it fails

The board offers one way to move a card: a drag, which needs a press held down while the pointer travels. WCAG 2.5.7 asks for a way to do the same with single clicks or taps, unless dragging is essential, and it isn't for moving a card to a column. The cards are list items with no focusable control, so nothing works from the keyboard either (2.1.1). And the move changes the page without a status message, so screen reader users are not told it happened (4.1.3).

## Who is affected

Some people can drag but slowly or unreliably: a long board, a scrolling column or a small drop zone turns a short move into a long one. A board is often the only place where work changes state, so a drag-only board shuts these people out of the whole workflow, not one page.

## What automation and AI miss

Scanners read the markup, and nothing in it is wrong: list items need no name, and `draggable="true"` is valid HTML. Whether a card can move without dragging only shows when someone tries. Drag libraries that use pointer events add no attribute at all. Asked to make the board accessible, AI tools often add `tabindex="0"` and `role="button"` to the cards, which puts them in the Tab order without giving the keyboard anything to do.

## Before: cards that move only by dragging

```html
<section class="column" data-column="todo">
  <h2>To do</h2>
  <ul>
    <li draggable="true" data-card="notes">Write release notes</li>
    <li draggable="true" data-card="pricing">Update the pricing page</li>
  </ul>
</section>
<!-- Doing and Done are the same: a heading over a list of cards -->
```

The only way to move a card is to drag it. A list item can't take focus or be pressed, and when the drop handler moves it, nothing is said.

## After: move buttons on each card

Keep the drag for people who like it, and give each card a button for each column it can go to. The board has a status region from the start, and the script fills it in after every move, by drag or by button.

```html
<li data-card="notes" draggable="true">
  <span id="card-notes">Write release notes</span>
  <button type="button" data-to="doing" aria-describedby="card-notes">Move to Doing</button>
  <button type="button" data-to="done" aria-describedby="card-notes">Move to Done</button>
</li>
…
<p role="status" class="board-status"></p>
```

```js
function moveCard(card, column) {
  column.querySelector('ul').append(card);
  updateButtons(card); // offer the columns it is not in now
  status.textContent = `Write release notes moved to ${columnName(column)}.`;
  card.querySelector('button').focus();
}
```

Each button moves the card with one click, tap or key press, and voice control users can say "Click Move to Done". The card's title is read as the button's description, so a screen reader says which card the button moves without changing its visible name. After the move, focus goes to the card in its new place, and the status line says where it went. This suits a board with a few columns: with more than three or four, the buttons crowd the card.

## After: a Move to list on each card

```html
<li data-card="notes" draggable="true">
  <span id="card-notes">Write release notes</span>
  <label for="move-notes">Move to</label>
  <select id="move-notes" aria-describedby="card-notes">
    <option value="doing">Doing</option>
    <option value="done">Done</option>
  </select>
  <button type="button" aria-describedby="card-notes">Move</button>
</li>
…
<p role="status" class="board-status"></p>
```

The list chooses the column and the button makes the move, so it takes clicks, not a drag. Keep the separate button: in some browsers the arrow keys change a closed select's value straight away, and a select that moved the card on `change` would move it at every arrow press. After the move, the script puts focus on the card's list in its new column and writes the card and its new column into the status region, as with any move. This fits any number of columns, and the list can also offer a position within the column.

## After: keyboard dragging with the arrow keys

```html
<p id="drag-help">To move a card, press Space to pick it up, then the arrow keys,
  then Space to drop it. Escape puts it back.</p>
<li data-card="notes" draggable="true">
  <button type="button" aria-describedby="drag-help">Write release notes</button>
</li>
…
<p role="status" class="board-status"></p>
```

Space on the card picks it up, Left and Right Arrow carry it to the next column, Space drops it and Escape returns it, and each step is announced in the status region. This passes 2.1.1 for keyboard users, and it keeps the spatial feel of the board. It does not pass 2.5.7 on its own: a person on a touch screen, with a head pointer or a single switch still has to drag, and voice control users have to say every arrow key. Many screen readers also keep the arrow keys for reading unless the user switches modes, so the picked-up card may not move. Add it to one of the other fixes, not instead of them.

## Which option to use

Both the move buttons and the Move to list pass on their own, for keyboard, pointer and screen reader users. Move buttons take one press and are the easiest to find, but they add a button per column to every card, so they suit boards with three or four columns. The Move to list takes two steps, but stays the same size however many columns there are, and can offer a position as well; choose it for larger boards. Keyboard dragging is worth adding when keyboard users ask for a faster way, but only beside one of the others.

## Implementation decisions

Keep the drag. A drag is fast for people who can do it, and 2.5.7 asks for an alternative, not a replacement. Keep the drag on the card and the controls inside it, so a press on a button never starts a drag.

The controls can sit on the card all the time, or appear in a menu from a single "Move" button. A menu is tidier, but it is a second pattern to build and test; make sure it opens on click and from the keyboard, not on hover.

Focus needs care, because moving a card moves its markup:

- Re-rendered cards: frameworks often destroy the card and create a new one in the new column, so focus falls to the body. Focus the new card's first control yourself after the move.
- The last column: when a card reaches Done, the button it was focused on may no longer exist. Focus the card's first remaining control, not a button for a column it is already in.
- Undo: if a move can be undone, put the Undo control in the card or in the status line, where focus already is.

Write one status message per move, short and in the same words as the columns: "Write release notes moved to Done." Don't announce while a card is being dragged with the mouse; announce the result.

## Verify the fix

1. Put the mouse aside. Press Tab and confirm you reach every card, or a control on it, in reading order.
2. Move a card to each other column using only clicks, taps or key presses. No step may need the pointer to move while pressed.
3. With a screen reader, move a card and confirm the card and its new column are announced once.
4. After each move made from the keyboard, confirm focus is on the moved card or one of its controls, and that Tab continues from there.
5. On a touch screen, move a card with taps only. With voice control, move one by saying a control's visible text.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the board component or its drag library changes.

## Limits

This is a starting pattern, not a guarantee for every application. Reordering cards within a column, nested boards and moves that need confirmation have their own needs, and are not covered here. Drag libraries differ in how they handle focus and announcements: test the complete journey with your target assistive technology.
