Section
Kanban Board — Sharp Glassmorphism
Frosted-glass project kanban board floating over a living gradient mesh. Translucent columns, luminous top-edge highlights, a cyan glow status dot, gradient priority chips, and count chips. Draggable task cards move between Backlog, In Progress, Review, and Done via the HTML5 Drag and Drop API, count badges update live, and an add-card affordance creates new tasks per column. Requires a colored backdrop for the glass to read.
- Section
- Tailwind
- HTML
- MIT
Preview
Live section
9:41 100%
devsnips.dev/library/tailwind/sections/app-ui/kanban-board/sharp-glassmorphism/ fluid · no fixed width 100%
Install
Add to your project
npx devsnips add Tailwind/Sections/App-UI/kanban-board/sharp-glassmorphism Tailwind/Sections/App-UI/kanban-board/sharp-glassmorphism <!--
Snippet Name: Kanban Board — Sharp Glassmorphism
Description: Frosted-glass kanban board over a vibrant gradient mesh, with glowing dots, count chips, draggable task cards (HTML5 DnD), and an add-card affordance per column.
Author: DevSnips Contributors
Usage Example: Drop into a vivid app shell as a project board widget. Drag cards between columns and click "+ Add card" to create new tasks. Fully wired in preview.html. Requires a colored backdrop behind the board for the glass to read.
-->
<div class="relative w-full" data-board="sg">
<!-- Glass board -->
<div class="relative overflow-hidden rounded-2xl border border-white/20 bg-white/10 p-4 shadow-[0_8px_40px_-12px_rgba(99,102,241,0.45)] backdrop-blur-2xl sm:p-5">
<!-- Top highlight -->
<div class="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white/50 to-transparent" aria-hidden="true"></div>
<div class="board-cols flex gap-4 overflow-x-auto pb-2" data-board-cols>
<!-- Column -->
<section class="sg-col w-[280px] shrink-0 rounded-xl border border-white/20 bg-white/10 backdrop-blur-md" aria-label="Backlog">
<header class="flex items-center justify-between border-b border-white/15 px-3.5 py-2.5">
<div class="flex items-center gap-2">
<span class="sg-dot"></span>
<h3 class="font-sora text-[13px] font-semibold text-white">Backlog</h3>
</div>
<span class="sg-count sg-mono rounded-full border border-white/20 bg-white/15 px-2 py-0.5 text-[11px] text-white backdrop-blur" data-count>3</span>
</header>
<div class="sg-list space-y-3 p-2.5" data-list>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-104">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-gradient-to-r from-fuchsia-500 to-pink-500 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white">P1</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-104</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">Wire dark-mode toggle to root class</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">Persist choice in localStorage and reflect on first paint.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">css</span>
<span class="sg-code">a11y</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">RM</div>
</div>
</article>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-118">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-white/15 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white backdrop-blur">P3</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-118</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">Sketch onboarding empty state</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">Three steps with inline illustrations and a CTA.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">design</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">JL</div>
</div>
</article>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-121">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-gradient-to-r from-indigo-500 to-violet-500 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white">P2</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-121</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">Research drag-drop a11y patterns</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">Compare native DnD vs roving tabindex keyboards.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">a11y</span>
<span class="sg-code">research</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">SK</div>
</div>
</article>
</div>
<button type="button" class="sg-add m-2.5 mt-0 flex w-[calc(100%-1.25rem)] items-center justify-center gap-1.5 rounded-lg border border-dashed border-white/25 bg-white/5 py-2 sg-mono text-[11px] text-white/75 backdrop-blur transition-colors hover:bg-white/15 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white/60">
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Add card
</button>
</section>
<!-- Column -->
<section class="sg-col w-[280px] shrink-0 rounded-xl border border-white/20 bg-white/10 backdrop-blur-md" aria-label="In Progress">
<header class="flex items-center justify-between border-b border-white/15 px-3.5 py-2.5">
<div class="flex items-center gap-2">
<span class="sg-dot"></span>
<h3 class="font-sora text-[13px] font-semibold text-white">In Progress</h3>
</div>
<span class="sg-count sg-mono rounded-full border border-white/20 bg-white/15 px-2 py-0.5 text-[11px] text-white backdrop-blur" data-count>2</span>
</header>
<div class="sg-list space-y-3 p-2.5" data-list>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-098">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-gradient-to-r from-fuchsia-500 to-pink-500 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white">P1</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-098</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">Ship kanban drag-and-drop widget</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">HTML5 DnD, move cards across columns, update counts live.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">js</span>
<span class="sg-code">widget</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">AB</div>
</div>
</article>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-110">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-gradient-to-r from-indigo-500 to-violet-500 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white">P2</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-110</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">Tokenize the 3-style palette</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">Extract shared tokens so variants stay cohesive.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">tokens</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">DT</div>
</div>
</article>
</div>
<button type="button" class="sg-add m-2.5 mt-0 flex w-[calc(100%-1.25rem)] items-center justify-center gap-1.5 rounded-lg border border-dashed border-white/25 bg-white/5 py-2 sg-mono text-[11px] text-white/75 backdrop-blur transition-colors hover:bg-white/15 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white/60">
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Add card
</button>
</section>
<!-- Column -->
<section class="sg-col w-[280px] shrink-0 rounded-xl border border-white/20 bg-white/10 backdrop-blur-md" aria-label="Review">
<header class="flex items-center justify-between border-b border-white/15 px-3.5 py-2.5">
<div class="flex items-center gap-2">
<span class="sg-dot"></span>
<h3 class="font-sora text-[13px] font-semibold text-white">Review</h3>
</div>
<span class="sg-count sg-mono rounded-full border border-white/20 bg-white/15 px-2 py-0.5 text-[11px] text-white backdrop-blur" data-count>1</span>
</header>
<div class="sg-list space-y-3 p-2.5" data-list>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-091">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-white/15 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white backdrop-blur">P3</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-091</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">Audit focus rings across sections</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">Ensure every interactive element has a visible focus-visible ring.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">a11y</span>
<span class="sg-code">audit</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">RM</div>
</div>
</article>
</div>
<button type="button" class="sg-add m-2.5 mt-0 flex w-[calc(100%-1.25rem)] items-center justify-center gap-1.5 rounded-lg border border-dashed border-white/25 bg-white/5 py-2 sg-mono text-[11px] text-white/75 backdrop-blur transition-colors hover:bg-white/15 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white/60">
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Add card
</button>
</section>
<!-- Column -->
<section class="sg-col w-[280px] shrink-0 rounded-xl border border-white/20 bg-white/10 backdrop-blur-md" aria-label="Done">
<header class="flex items-center justify-between border-b border-white/15 px-3.5 py-2.5">
<div class="flex items-center gap-2">
<span class="sg-dot"></span>
<h3 class="font-sora text-[13px] font-semibold text-white/70">Done</h3>
</div>
<span class="sg-count sg-mono rounded-full border border-white/20 bg-white/15 px-2 py-0.5 text-[11px] text-white backdrop-blur" data-count>2</span>
</header>
<div class="sg-list space-y-3 p-2.5" data-list>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-087">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-white/15 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white backdrop-blur">P3</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-087</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white/70 line-through decoration-white/40">Set up CDN + fonts scaffold</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/50">Tailwind play CDN + Google Fonts across variants.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">infra</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">JL</div>
</div>
</article>
<article class="sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing" draggable="true" tabindex="0" data-id="SNIP-074">
<div class="mb-2 flex items-center justify-between gap-2">
<span class="sg-mono rounded-md border border-white/30 bg-white/15 px-1.5 py-0.5 text-[10px] font-medium uppercase text-white backdrop-blur">P3</span>
<span class="sg-mono text-[10px] text-white/55">SNIP-074</span>
</div>
<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white/70 line-through decoration-white/40">Write STYLE_TOKENS reference</h4>
<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/50">Canonical token doc for all three design styles.</p>
<div class="mt-3 flex items-center justify-between">
<div class="flex flex-wrap items-center gap-1">
<span class="sg-code">docs</span>
</div>
<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">SK</div>
</div>
</article>
</div>
<button type="button" class="sg-add m-2.5 mt-0 flex w-[calc(100%-1.25rem)] items-center justify-center gap-1.5 rounded-lg border border-dashed border-white/25 bg-white/5 py-2 sg-mono text-[11px] text-white/75 backdrop-blur transition-colors hover:bg-white/15 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white/60">
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Add card
</button>
</section>
</div>
</div>
<style>
[data-board="sg"] .sg-mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
[data-board="sg"] .font-sora { font-family: 'Sora', sans-serif; }
[data-board="sg"] .sg-code { font-family: 'JetBrains Mono', monospace; font-size: 11px; background: rgba(255,255,255,0.18); padding: 1px 6px; border-radius: 5px; color: #fff; backdrop-filter: blur(4px); }
[data-board="sg"] .sg-dot { display: inline-block; height: 6px; width: 6px; border-radius: 9999px; background: #6ee7ff; box-shadow: 0 0 8px #6ee7ff; }
[data-board="sg"] .sg-glow { box-shadow: 0 0 0 1px rgba(255,255,255,0.08), 0 4px 20px -4px rgba(168,85,247,0.5); }
[data-board="sg"] .sg-list { min-height: 48px; transition: background-color .12s ease; border-radius: 10px; }
[data-board="sg"] .sg-list.sg-over { background-color: rgba(168,85,247,0.18); outline: 1px dashed rgba(255,255,255,0.55); outline-offset: -2px; }
[data-board="sg"] .sg-card.sg-drag { opacity: .35; }
[data-board="sg"] .sg-card:focus { outline: 2px solid rgba(255,255,255,0.6); outline-offset: 2px; }
[data-board="sg"] .sg-placeholder { border: 1px dashed rgba(255,255,255,0.45); background: rgba(255,255,255,0.06); height: 82px; border-radius: 12px; }
[data-board="sg"] .board-cols { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.25) transparent; }
[data-board="sg"] .board-cols::-webkit-scrollbar { height: 8px; }
[data-board="sg"] .board-cols::-webkit-scrollbar-track { background: transparent; }
[data-board="sg"] .board-cols::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.22); border-radius: 9999px; }
</style>
<script>
(function () {
var root = document.currentScript.closest('[data-board="sg"]');
if (!root) return;
var cols = root.querySelectorAll('[data-list]');
var dragged = null;
var placeholder = null;
var uid = 0;
function updateCounts() {
root.querySelectorAll('.sg-col').forEach(function (col) {
var count = col.querySelector('[data-list]').children.length;
var badge = col.querySelector('[data-count]');
if (badge) badge.textContent = count;
});
}
function makePlaceholder() {
var p = document.createElement('div');
p.className = 'sg-placeholder';
return p;
}
function clearOver() {
cols.forEach(function (l) { l.classList.remove('sg-over'); });
}
function attachCard(card) {
card.addEventListener('dragstart', function (e) {
dragged = card;
card.classList.add('sg-drag');
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move';
try { e.dataTransfer.setData('text/plain', card.dataset.id || ''); } catch (err) {}
}
placeholder = makePlaceholder();
if (card.parentNode) card.parentNode.insertBefore(placeholder, card.nextSibling);
});
card.addEventListener('dragend', function () {
card.classList.remove('sg-drag');
if (placeholder && placeholder.parentNode) placeholder.parentNode.removeChild(placeholder);
placeholder = null;
dragged = null;
clearOver();
updateCounts();
});
}
root.querySelectorAll('.sg-card').forEach(attachCard);
cols.forEach(function (list) {
list.addEventListener('dragover', function (e) {
e.preventDefault();
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
list.classList.add('sg-over');
if (!dragged || !placeholder) return;
var after = null;
var cards = list.querySelectorAll('.sg-card:not(.sg-drag)');
Array.prototype.forEach.call(cards, function (c) {
var r = c.getBoundingClientRect();
if (e.clientY < r.top + r.height / 2) { after = c; }
});
if (after) { list.insertBefore(placeholder, after); }
else if (list !== dragged.parentNode || list.lastElementChild !== placeholder) { list.appendChild(placeholder); }
});
list.addEventListener('dragleave', function (e) {
if (!list.contains(e.relatedTarget)) list.classList.remove('sg-over');
});
list.addEventListener('drop', function (e) {
e.preventDefault();
if (dragged && placeholder && placeholder.parentNode === list) {
list.insertBefore(dragged, placeholder);
if (placeholder.parentNode) placeholder.parentNode.removeChild(placeholder);
} else if (dragged) {
list.appendChild(dragged);
}
placeholder = null;
dragged = null;
clearOver();
updateCounts();
});
});
var avatars = ['AB', 'CD', 'EF', 'GH', 'JK', 'MN'];
var grad = ['from-fuchsia-500 to-pink-500', 'from-indigo-500 to-violet-500', 'from-cyan-500 to-blue-500', 'from-emerald-500 to-teal-500'];
root.querySelectorAll('.sg-add').forEach(function (btn) {
btn.addEventListener('click', function () {
var col = btn.closest('.sg-col');
var list = col.querySelector('[data-list]');
uid += 1;
var id = 'SNIP-' + (200 + uid);
var g = grad[uid % grad.length];
var card = document.createElement('article');
card.className = 'sg-card group cursor-grab rounded-xl border border-white/20 bg-white/15 p-3 backdrop-blur transition-colors hover:bg-white/25 active:cursor-grabbing';
card.setAttribute('draggable', 'true');
card.setAttribute('tabindex', '0');
card.dataset.id = id;
card.innerHTML =
'<div class="mb-2 flex items-center justify-between gap-2">' +
'<span class="sg-mono rounded-md border border-white/30 bg-gradient-to-r ' + g + ' px-1.5 py-0.5 text-[10px] font-medium uppercase text-white">P2</span>' +
'<span class="sg-mono text-[10px] text-white/55">' + id + '</span>' +
'</div>' +
'<h4 class="font-sora text-[13.5px] font-semibold leading-snug text-white">New task — define the next step</h4>' +
'<p class="mt-1 font-sora text-[12px] leading-relaxed text-white/70">Click to flesh out details, owners, and tags.</p>' +
'<div class="mt-3 flex items-center justify-between">' +
'<div class="flex flex-wrap items-center gap-1"><span class="sg-code">new</span></div>' +
'<div class="sg-glow flex h-6 w-6 items-center justify-center rounded-full border border-white/30 bg-white/25 font-sora text-[10px] font-semibold text-white backdrop-blur">' + avatars[uid % avatars.length] + '</div>' +
'</div>';
list.appendChild(card);
attachCard(card);
updateCounts();
card.focus();
});
});
// Keyboard reordering: ArrowUp/Down moves within a column,
// ArrowLeft/Right moves the card to the previous/next column.
// Reuses the same DOM-move + updateCounts() logic as drag-and-drop.
root.querySelectorAll('.sg-card').forEach(function (card) {
card.addEventListener('keydown', function (e) {
var list = card.parentNode;
if (!list || !list.hasAttribute('data-list')) return;
var colsArr = Array.prototype.slice.call(cols);
var colIdx = colsArr.indexOf(list);
var cards = Array.prototype.slice.call(list.querySelectorAll('.sg-card'));
var idx = cards.indexOf(card);
function moveTo(targetList) {
targetList.appendChild(card); updateCounts(); card.focus();
}
switch (e.key) {
case 'ArrowUp':
e.preventDefault();
if (idx > 0) { list.insertBefore(card, cards[idx - 1]); updateCounts(); card.focus(); }
break;
case 'ArrowDown':
e.preventDefault();
if (idx < cards.length - 1) { list.insertBefore(card, cards[idx + 1].nextSibling); updateCounts(); card.focus(); }
break;
case 'ArrowLeft':
e.preventDefault();
if (colIdx > 0) { moveTo(colsArr[colIdx - 1]); }
break;
case 'ArrowRight':
e.preventDefault();
if (colIdx < colsArr.length - 1) { moveTo(colsArr[colIdx + 1]); }
break;
}
});
});
updateCounts();
})();
</script>
</div> 378 lines UTF-8 · LF · Spaces: 2
Continue browsing