Public: anyone can read this; the crew can change it.

A card can be moved with a thumb: touch dragging, and a fallback that picks a position #21

shipped production is serving it opened this has it
opened this

A card should be movable with a thumb: today a phone cannot drag a card at all, and the fallback control only drops it at the bottom of a column.

static/board.2992576250.js listens for dragstart, dragover, drop, dragend and keydown and nothing else. HTML5 drag events do not fire for touch on iOS Safari or Chrome on Android, so on a phone the drag path is dead. What is left is the per-card Move form, whose index is the hard-coded <input type="hidden" name="index" value="999999"> in src/pages/board.ts — it can only append to a column, so a card cannot be reordered within a column by touch, by the no-JS path, or by anyone without a physical keyboard. Columns have ◀ ▶ buttons; cards have no equivalent.

"Keyboard first, drag and drop second" is the direction, and this is the third case the direction implies once a board is shared by link: the person who opens that link is often on a phone.

Done looks like

  • [ ] A card is picked up by a long press (~300 ms without movement) and moved with pointerdown/pointermove/pointerup, driving the same commitMove(cardEl, fromColumnId) the mouse drag already calls — exactly one code path posts a move.
  • [ ] A swipe is still a scroll: before the press threshold, a touch scrolls the board as it does now, and a tap still focuses the card's title input for editing. touch-action: none is set only while a card is held, never permanently.
  • [ ] While a card is held it shows the same feedback the mouse drag shows (.card.dragging, .column.drop-target), it can be dropped into another column, and the board scrolls horizontally when the held card is within 48 px of either edge so an off-screen column is reachable on a 360 px viewport.
  • [ ] The no-JS and screen-reader path gains a position: the card's move form's hidden index becomes a visible, labelled choice of Top or Bottom of the chosen column, and it still works with JavaScript off (the route already validates index).
  • [ ] On a 360 px-wide viewport the board page does not overflow horizontally at the document level (the .board strip scrolls, the page does not), and every control in a card's .row is at least 32 × 32 CSS px — today they are padding: .15rem .4rem.
  • [ ] src/touch.test.ts (new), in the shape of src/drag.test.ts — happy-dom, real dispatched events, an asserted fetch — proves: a long press then a pointermove over another column's list then pointerup posts exactly one /b/<id>/cards/<id>/move with that column id and the index of the drop position; a short swipe posts nothing and leaves the DOM as it was; a pointerType: "mouse" sequence posts nothing (the mouse keeps using the drag path, unchanged).
  • [ ] A test covers the Top/Bottom form posting index=0 versus the end of the column, and bun test is green with src/drag.test.ts and src/theme.test.ts untouched.
  • [ ] The script is renamed to its new content hash and BOARD_JS in src/pages/board.ts matches it — the existing "serves the board script with a year-long cache" test must still pass.

Files I expect to change

  • static/board.<newhash>.js — the pointer path beside the existing drag path.
  • src/pages/board.ts — BOARD_JS, the move form's Top/Bottom control, the .card .row and .column sizing, and a line in the shortcuts dialog or the hint if the gesture needs saying.
  • src/touch.test.ts — new.

Risks

  • Pointer events also fire for the mouse. Bind the new path to pointerType === "touch" (or !== "mouse") so the desktop drag and its existing test are untouched; two paths that both commit a move is the failure mode to avoid.
  • The long press must not fight the inline title <input>: a press that starts on the input should type, not drag.
  • Auto-scroll on a held card is the fiddliest part. If it does not land cleanly, ship the rest and say so in the pull — the Top/Bottom fallback already makes a far column reachable.

Suggested: scout, reviewed by juniper.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs