Swap-on-Drop Tiles
A copy-paste drag & drop component in pure HTML & CSS. Zero dependencies, framework-agnostic, MIT-licensed.
Drag & DropHTMLCSSany framework
Drag a tile onto another to swap places. With focus, press Alt plus Arrow Left or Arrow Right.
A
B
Copy into your project
HTML
<!-- Swap-on-Drop Tiles — dropping tile A onto tile B swaps their DOM order.
Keyboard: focus a tile, Alt+ArrowLeft/ArrowRight swaps with the neighbor. -->
<div class="nuda-dd2-swap-tiles" role="group" aria-label="Swap tiles by dragging" aria-describedby="dd2-swap-tiles-desc">
<p id="dd2-swap-tiles-desc" class="nuda-dd2-swap-tiles__sr">Drag a tile onto another to swap places. With focus, press Alt plus Arrow Left or Arrow Right.</p>
<div class="nuda-dd2-swap-tiles__tile nuda-dd2-swap-tiles__tile--a" role="option" aria-selected="true" tabindex="0">A</div>
<div class="nuda-dd2-swap-tiles__tile nuda-dd2-swap-tiles__tile--b" role="option" aria-selected="false" tabindex="0">B</div>
</div>CSS
.nuda-dd2-swap-tiles {
position: relative;
width: 160px;
height: 70px;
}
.nuda-dd2-swap-tiles__sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
.nuda-dd2-swap-tiles__tile {
position: absolute;
top: 9px;
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
font: 800 16px ui-sans-serif,system-ui;
cursor: grab;
}
.nuda-dd2-swap-tiles__tile:focus-visible {
outline: 2px solid #e4ff54;
outline-offset: 2px;
}
.nuda-dd2-swap-tiles__tile--a {
left: 24px;
background: rgba(228,255,84,.12);
border: 1.5px solid #e4ff54;
color: #e4ff54;
animation: nuda-dd2-swap-tiles-a 3s cubic-bezier(.4,0,.2,1) infinite;
}
.nuda-dd2-swap-tiles__tile--b {
right: 24px;
background: #161616;
border: 1.5px solid rgba(255,255,255,.12);
color: #cfcfcf;
animation: nuda-dd2-swap-tiles-b 3s cubic-bezier(.4,0,.2,1) infinite;
}
@keyframes nuda-dd2-swap-tiles-a {
0%,10%,90%,100% {
transform: translateX(0) scale(1);
box-shadow: none;
}
50% {
transform: translateX(64px) scale(1.08);
box-shadow: 0 10px 22px -8px rgba(228,255,84,.5);
}
}
@keyframes nuda-dd2-swap-tiles-b {
0%,10%,90%,100% {
transform: translateX(0) scale(1);
}
50% {
transform: translateX(-64px) scale(.96);
}
}
@media (prefers-reduced-motion:reduce) {
.nuda-dd2-swap-tiles__tile--a,.nuda-dd2-swap-tiles__tile--b {
animation: none;
}
}
How to use Swap-on-Drop Tiles
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.