<ol id="$demo">
<li draggable="true" style="view-transition-name: card-1">CSS</li>
<li draggable="true" style="view-transition-name: card-2">HTML</li>
<li draggable="true" style="view-transition-name: card-3">JS</li>
<li draggable="true" style="view-transition-name: yes-this-keeps-animating-during-morph">
<picture>
<source srcset="https://fonts.gstatic.com/s/e/notoemoji/latest/1f92f/512.webp" type="image/webp">
<img src="https://fonts.gstatic.com/s/e/notoemoji/latest/1f92f/512.gif" alt="🤯" width="125" height="125">
</picture>
</li>
<li draggable="true" style="view-transition-name: a-really-long-list-item-for-funsies">If you're gonna build a time machine into a car, why not do it with some style?</li>
</ol>
@import "https://unpkg.com/open-props" layer(design.system);
@import "https://unpkg.com/open-props/normalize.min.css" layer(demo.support);
@layer demo {
@keyframes drop-zone {
from { box-shadow: 0 0 0 0 var(--link) }
to { box-shadow: 0 0 0 25px #0000 }
}
li.over {
outline: 2px dashed var(--link);
@media (prefers-reduced-motion: no-preference) {
animation: drop-zone 1.5s var(--ease-out-5) infinite;
}
> * {
pointer-events: none;
}
}
[draggable] {
cursor: grab;
user-select: none;
&:active {
cursor: grabbing;
}
}
/*
optimization:
disguises the crossfade on the gif,
show the live gif always
*/
::view-transition-old(yes-this-keeps-animating-during-morph) {
display: none;
}
::view-transition-new(yes-this-keeps-animating-during-morph) {
animation: none;
}
html {
view-transition-name: none;
}
}
@layer demo.support {
body {
display: grid;
place-content: center;
padding: var(--size-5);
gap: var(--size-5);
}
ol {
display: grid;
gap: var(--size-3);
justify-items: start;
}
ol > li {
background: var(--surface-2);
border: 1px solid transparent;
padding-block: var(--size-3);
padding-inline: var(--size-4);
border-radius: var(--radius-3);
box-shadow: var(--shadow-4);
@media (prefers-color-scheme: light) {
background: white;
}
}
}
const drag = {
src: null,
items: $demo.querySelectorAll(':scope > li'),
}
function handleDrop(e) {
e?.stopPropagation()
if (drag.src != this) {
if (document.startViewTransition)
document.startViewTransition(() =>
swapSiblings(drag.src, this))
else
swapSiblings(drag.src, this)
}
}
function handleDragStart(e) {
requestAnimationFrame(() => {
this.style.opacity = '.4'
})
e.dataTransfer.setData("text/html", this.outerHTML)
drag.src = this
// e.dataTransfer.effectAllowed = 'move'
}
function handleDragOver(e) {
e?.preventDefault()
// e.dataTransfer.dropEffect = 'move'
}
function handleDragEnter(e) {
this.classList.add('over')
}
function handleDragLeave(e) {
this.classList.remove('over')
}
function handleDragEnd(e) {
this.style.opacity = '1'
drag.items.forEach(item => {
item.classList.remove('over')
})
}
function swapSiblings(sib1, sib2) {
let p1 = sib1.previousSibling
let p2 = sib2.previousSibling
p1.after(sib2)
p2.after(sib1)
}
drag.items.forEach(item => {
item.addEventListener('dragstart', handleDragStart, false)
item.addEventListener('dragenter', handleDragEnter, false)
item.addEventListener('dragover', handleDragOver, false)
item.addEventListener('dragleave', handleDragLeave, false)
item.addEventListener('drop', handleDrop, false)
item.addEventListener('dragend', handleDragEnd, false)
})
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.