Drag to Reorder
Three chips that can be drag-reordered with real pointer math — no library. Keyboard accessible: Tab to focus, ←/→ to swap with neighbour. The full reorder pattern.
Drag to Reorder the 13th of 20 designs in the 20 CSS Tags & Chips Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<ul class="ctc-drag" role="listbox" aria-label="Reorder tags"> <li class="ctc-drag-chip" tabindex="0" data-ctc-drag>Frontend</li> <li class="ctc-drag-chip" tabindex="0" data-ctc-drag>Backend</li> <li class="ctc-drag-chip" tabindex="0" data-ctc-drag>DevOps</li> </ul>
.ctc-drag {
display: flex;
gap: 8px;
list-style: none;
margin: 0;
padding: 0;
}
.ctc-drag-chip {
display: inline-flex;
align-items: center;
padding: 8px 16px;
background: #1f1f2e;
color: #c4b5fd;
border: 1px solid rgba(124, 108, 255, 0.35);
border-radius: 999px;
font:
600 12px/1 system-ui,
sans-serif;
cursor: grab;
user-select: none;
touch-action: none;
transition:
transform 0.18s ease,
background 0.2s;
}
.ctc-drag-chip:hover {
background: rgba(124, 108, 255, 0.18);
}
.ctc-drag-chip.is-dragging {
cursor: grabbing;
background: #7c6cff;
color: #fff;
z-index: 2;
transition: none;
}
.ctc-drag-chip:focus-visible {
outline: 2px solid #a78bfa;
outline-offset: 2px;
} (function () {
document.querySelectorAll(".ctc-drag").forEach(function (list) {
var dragged = null;
list.querySelectorAll("[data-ctc-drag]").forEach(function (chip) {
chip.addEventListener("pointerdown", function (e) {
dragged = chip;
chip.classList.add("is-dragging");
chip.setPointerCapture(e.pointerId);
});
chip.addEventListener("pointermove", function (e) {
if (dragged !== chip) return;
var siblings = Array.from(list.children).filter(function (c) {
return c !== chip;
});
for (var i = 0; i < siblings.length; i++) {
var rect = siblings[i].getBoundingClientRect();
if (e.clientX > rect.left && e.clientX < rect.right) {
var dragRect = chip.getBoundingClientRect();
if (e.clientX < rect.left + rect.width / 2) {
list.insertBefore(chip, siblings[i]);
} else {
list.insertBefore(chip, siblings[i].nextSibling);
}
break;
}
}
});
chip.addEventListener("pointerup", function () {
chip.classList.remove("is-dragging");
dragged = null;
});
chip.addEventListener("keydown", function (e) {
var siblings = Array.from(list.children);
var idx = siblings.indexOf(chip);
if (e.key === "ArrowLeft" && idx > 0) {
e.preventDefault();
list.insertBefore(chip, siblings[idx - 1]);
chip.focus();
} else if (e.key === "ArrowRight" && idx < siblings.length - 1) {
e.preventDefault();
list.insertBefore(chip, siblings[idx + 1].nextSibling);
chip.focus();
}
});
});
});
})();