Zones and the card template are declared in HTML. The only JS left is the pile β and that comes from your data
anyway. Add, change or remove a <trieur-zone> mid-session: the stage follows without
starting over, the current pile and the undo history survive.
<trieur-deck id="sorter" layout="voronoi" threshold="80" multi
tap-zones="false" touch-preview="false">
<trieur-zone value="dev" key="a" icon="β" color="#4a54f2">dev</trieur-zone>
<trieur-zone value="ai" icon="β">ai</trieur-zone>
<trieur-zone></trieur-zone><!-- free zone: dropping fires trieur:assign -->
<template data-card>
<div class="link">
<span class="host" data-field="host"></span>
<h4 data-field="title"></h4>
<img data-field="image" data-attr="src" alt="" />
</div>
</template>
</trieur-deck>
<script type="module">
import '@trieur/core/element';
sorter.items = links; // JS always wins over markup
sorter.addEventListener('trieur:sort', (e) => console.log(e.detail.item, e.detail.zones));
</script>
No shadow DOM, deliberately: you draw the cards, so your styles have to reach them. The stage lives in a child of
the custom element, so the library can rewrite its container without erasing the
<trieur-zone> elements that are its configuration.