Reorder List
Reorder a vertical queue with pointer or keyboard input while the application owns persistence.
Use c2-reorder-list for one vertical queue whose ordinary direct children need pointer and keyboard reordering. The component keeps the committed visual order immediately; listen for reorder and persist event.detail.order in application state. It never physically moves application-owned light DOM.
Add swipeable and each row also slides sideways to reveal actions behind it, a red Delete by default. Choose another pattern for grids, transfer between lists, nesting, copying, or undo history.
Import
import '@c2n/components/reorder-list'Usage
Ordinary children need no slot. Optional non-empty data-reorder-key values identify application records and must be unique. Use data-reorder-label for concise announcements. data-fixed pins a row at its absolute position; literal data-fixed="false" and data-fixed="0" remain movable.
Pointer users press a movable row, cross the pickup threshold, and release at the placeholder. Whole-row touch reordering owns vertical direct manipulation; the nearest eligible scroll container moves automatically unless auto-scroll-disabled is set. Buttons, links, inputs, and editable descendants keep their native behavior.
Keyboard users focus a movable placement and press Space to pick it up. ArrowUp/ArrowDown move one permitted destination, Home/End jump, Space commits, and Escape cancels. Supply aria-label or aria-labelledby for an editable list when context does not already name it.
Listen for the canonical event:
const queue = document.querySelector('c2-reorder-list')
queue.addEventListener('reorder', (event) => {
const keys = event.detail.order.map((item) => item.key)
saveQueue(keys)
})reorder fires once only after a real change and includes the moved element/key, absolute fromIndex/toIndex, complete resulting order, and mouse, touch, pen, or keyboard input method. Duplicate keys fire reorder-error and block pickup. Initialization, external child reconciliation, cancellation, threshold-only gestures, and no-op drops fire no success event.
The deprecated change event still accompanies a successful reorder with its legacy numeric permutation. Migrate persistence code to reorder before the next explicitly breaking release.
Swipe actions
swipeable lets each item slide sideways. A horizontal drag swipes and a vertical one still reorders, so the two work in
the same list. Swiping reveals the actions of that side, set with swipe-actions (a JSON array in the attribute, or
the swipeActions property):
queue.swipeActions = [
{ id: 'archive', label: 'Archive', tone: 'warning', key: 'e' }, // end side, the outermost
{ id: 'delete', label: 'Delete', tone: 'danger', key: 'Delete' },
{ id: 'done', label: 'Done', side: 'start', tone: 'success', icon: 'check' },
]
queue.addEventListener('swipe-action', (event) => {
const { action, item } = event.detail
if (action.id === 'archive') archive(item.key)
})An action takes an id, a label, an optional side (end, revealed by swiping towards the start edge, is the
default; start is revealed by swiping the other way), a tone (danger, warning, success, accent or neutral),
a built-in icon (delete, archive, check, flag, pin, or none) and a key that runs it on a focused item.
The first action of a side is its outermost one: swipe past half the row and let go to run it without tapping.
Every action fires swipe-action with the item, the action, and whether it came from its button, a full swipe or its
key. Only delete does something by itself: it removes the item element. Call event.preventDefault() to keep the
item and delete it from your own data instead, which is what a framework that renders the rows should do.
A revealed item closes again when it is tapped, swiped back, when anything else is tapped, or on Escape, so nothing
happens unless an action is chosen. Keyboard users reach the same actions through their keys, which the list announces
on each item; the revealed buttons are focusable too. data-swipe="false" keeps an item from swiping.
Style consumer-owned rows directly. Use container, item, placeholder, dragging-item, swipe-content, swipe-actions and swipe-action parts for component-owned boundaries; documented CSS variables cover spacing, dividers, feedback, focus, active opacity, shadow, and motion. Reduced-motion mode removes decorative interpolation while preserving the interaction.