Reorder Readback

Reads back every reorder with its consequence and an Undo, so a slipped drag doesn't silently change priorities, fallbacks or steps.

StopsSlipped drag reordered payment fallbacks

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/reorder-readback.json

The files land in your project. They're yours to edit.

Usage

reorder-readback-usage.tsx
import { ReorderReadback, type ReorderItem } from "@/components/ui/reorder-readback"

<ReorderReadback
  label="Payment fallback order"
  items={gateways}
  value={order}
  onValueChange={setOrder}
  baseline={saved}
  describePosition={(i, total) => (i === 1 ? "the first gateway tried for cards" : i === total - 1 ? "the last resort" : `fallback ${i}`)}
/>

What it catches

  • Every move, read back with what the new position means
  • Moves that would push a pinned row aside, refused with a reason
  • Rows moved since the last saved baseline
  • Touch scrolling that would start a drag (drag only from the grip)

Props

ReorderReadback props
PropTypeDefault
items* (required)

Rows to order, each with an id and a name.

ReorderItem[]—
value

Order as a list of ids (controlled).

string[]—
defaultValue

Initial order when uncontrolled. Defaults to the order of `items`.

string[]—
onValueChange

Fires once per finished move (on drop, never mid-drag), with the new order.

(order: string[]) => void—
baseline

The last saved order. Rows that moved since show ↑/↓ and the header offers Revert.

string[]—
describePosition

What a place means, so the readback says the consequence: `(i) => i === 0 ? "tried first" : ""`.

(index: number, total: number) => string—
formatPosition

Position wording, "3rd" by default.

(index: number) => stringordinal
label

Accessible name of the list.

string"Order"
messages

Visible text, all overridable.

Partial<typeof REORDER_READBACK_MESSAGES>—

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Space picks up, arrow keys move, Space drops and Escape cancels; instructions linked via aria-describedby
  • Move-up/down buttons on every row give a non-drag alternative (WCAG 2.2 SC 2.5.7)
  • One role=status region with debounced announcements
  • Grip has an aria-label and aria-pressed; pinned rows use aria-disabled with a lock icon
  • Animations respect reduced motion; focus returns to the grip after moves and undo

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Consequence text comes from your describePosition; without it only positions are read back
  • Does not save; persist the order yourself (normalizeOrder in reorder.ts can run on the server)

What it stops

Slipped drag reordered payment fallbacks

Also known as

drag to reorder · sortable · dnd-kit · priority list · reorder · payment gateway order · razorpay · drag handle

Dependencies

Pairs well with