Section

Kanban Board — Neo-Brutalism

Brutalist project kanban board with hard black borders, offset drop shadows, and flat bright column headers. 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. Press-down hover states and monospace labels keep the raw, editorial feel.

  • Section
  • Tailwind
  • HTML
  • MIT

Preview

Live section

Interactive preview
Open preview
9:41 100%
devsnips.dev/library/tailwind/sections/app-ui/kanban-board/neo-brutalism/
fluid · no fixed width 100%
Dependencies: Tailwind CSS (CDN) · Google Fonts: Archivo, JetBrains Mono Responsive · Keyboard-ready

Install

Add to your project

terminal
npx devsnips add Tailwind/Sections/App-UI/kanban-board/neo-brutalism
registry path: Tailwind/Sections/App-UI/kanban-board/neo-brutalism

Source

Implementation

code.html
<!--
Snippet Name: Kanban Board — Neo-Brutalism
Description: Hard-bordered kanban board with offset shadows, count badges, draggable task cards (HTML5 DnD), and an add-card affordance per column.
Author: DevSnips Contributors
Usage Example: Drop into any app shell as a project board widget. Drag cards between columns and click "+ ADD CARD" to create new tasks. Fully wired in preview.html.
-->
<div class="relative w-full" data-board="nb">

  <!-- Board -->
  <div class="border-2 border-black bg-white p-4 shadow-[8px_8px_0_0_#000] sm:p-5">
    <div class="board-cols flex gap-4 overflow-x-auto pb-2" data-board-cols>

      <!-- Column -->
      <section class="nb-col w-[280px] shrink-0 border-2 border-black bg-[#FFFDF5]" aria-label="Backlog">
        <header class="flex items-center justify-between border-b-2 border-black bg-[#FFE600] px-3 py-2">
          <h3 class="font-archivo text-sm font-extrabold uppercase tracking-tight">Backlog</h3>
          <span class="nb-count border-2 border-black bg-white px-2 font-mono text-[11px] font-bold shadow-[2px_2px_0_0_#000]" data-count>3</span>
        </header>
        <div class="nb-list space-y-3 p-3" data-list>
          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#FF4FA3] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase text-white">P1</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-104</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug">Wire dark-mode toggle to root class</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Persist choice in localStorage and reflect on first paint.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-[#00C2FF] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">css</span>
                <span class="border border-black bg-[#00E676] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">a11y</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#FFE600] font-archivo text-[11px] font-extrabold">RM</div>
            </div>
          </article>

          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#00C2FF] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase text-black">P3</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-118</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug">Sketch onboarding empty state</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Three steps with inline illustrations and a CTA.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-white px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">design</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#FF4FA3] font-archivo text-[11px] font-extrabold text-white">JL</div>
            </div>
          </article>

          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#FF4FA3] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase text-white">P2</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-121</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug">Research drag-drop a11y patterns</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Compare native DnD vs roving tabindex keyboards.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-[#00E676] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">a11y</span>
                <span class="border border-black bg-white px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">research</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-black font-archivo text-[11px] font-extrabold text-white">SK</div>
            </div>
          </article>
        </div>
        <button type="button" class="nb-add m-3 mt-0 flex w-[calc(100%-1.5rem)] items-center justify-center gap-1.5 border-2 border-black bg-white py-2 font-mono text-[11px] font-bold uppercase shadow-[2px_2px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2">
          <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
          Add card
        </button>
      </section>

      <!-- Column -->
      <section class="nb-col w-[280px] shrink-0 border-2 border-black bg-[#FFFDF5]" aria-label="In Progress">
        <header class="flex items-center justify-between border-b-2 border-black bg-[#00C2FF] px-3 py-2">
          <h3 class="font-archivo text-sm font-extrabold uppercase tracking-tight">In Progress</h3>
          <span class="nb-count border-2 border-black bg-white px-2 font-mono text-[11px] font-bold shadow-[2px_2px_0_0_#000]" data-count>2</span>
        </header>
        <div class="nb-list space-y-3 p-3" data-list>
          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#FF4FA3] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase text-white">P1</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-098</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug">Ship kanban drag-and-drop widget</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">HTML5 DnD, move cards across columns, update counts live.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-[#FFE600] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">js</span>
                <span class="border border-black bg-white px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">widget</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#00E676] font-archivo text-[11px] font-extrabold">AB</div>
            </div>
          </article>

          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#FFE600] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">P2</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-110</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug">Tokenize the 3-style palette</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Extract shared tokens so variants stay cohesive.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-[#FF4FA3] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase text-white">tokens</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#00C2FF] font-archivo text-[11px] font-extrabold">DT</div>
            </div>
          </article>
        </div>
        <button type="button" class="nb-add m-3 mt-0 flex w-[calc(100%-1.5rem)] items-center justify-center gap-1.5 border-2 border-black bg-white py-2 font-mono text-[11px] font-bold uppercase shadow-[2px_2px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2">
          <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
          Add card
        </button>
      </section>

      <!-- Column -->
      <section class="nb-col w-[280px] shrink-0 border-2 border-black bg-[#FFFDF5]" aria-label="Review">
        <header class="flex items-center justify-between border-b-2 border-black bg-[#FF4FA3] px-3 py-2">
          <h3 class="font-archivo text-sm font-extrabold uppercase tracking-tight text-white">Review</h3>
          <span class="nb-count border-2 border-black bg-white px-2 font-mono text-[11px] font-bold shadow-[2px_2px_0_0_#000]" data-count>1</span>
        </header>
        <div class="nb-list space-y-3 p-3" data-list>
          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#00E676] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">P3</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-091</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug">Audit focus rings across sections</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Ensure every interactive element has a visible focus-visible ring.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-[#00E676] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">a11y</span>
                <span class="border border-black bg-white px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">audit</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#FFE600] font-archivo text-[11px] font-extrabold">RM</div>
            </div>
          </article>
        </div>
        <button type="button" class="nb-add m-3 mt-0 flex w-[calc(100%-1.5rem)] items-center justify-center gap-1.5 border-2 border-black bg-white py-2 font-mono text-[11px] font-bold uppercase shadow-[2px_2px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2">
          <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
          Add card
        </button>
      </section>

      <!-- Column -->
      <section class="nb-col w-[280px] shrink-0 border-2 border-black bg-[#FFFDF5]" aria-label="Done">
        <header class="flex items-center justify-between border-b-2 border-black bg-[#00E676] px-3 py-2">
          <h3 class="font-archivo text-sm font-extrabold uppercase tracking-tight">Done</h3>
          <span class="nb-count border-2 border-black bg-white px-2 font-mono text-[11px] font-bold shadow-[2px_2px_0_0_#000]" data-count>2</span>
        </header>
        <div class="nb-list space-y-3 p-3" data-list>
          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#00C2FF] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">P3</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-087</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug line-through decoration-black decoration-2">Set up CDN + fonts scaffold</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Tailwind play CDN + Google Fonts across variants.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-[#FFE600] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">infra</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#FF4FA3] font-archivo text-[11px] font-extrabold text-white">JL</div>
            </div>
          </article>

          <article class="nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] active:cursor-grabbing" draggable="true" tabindex="0">
            <div class="mb-2 flex items-center justify-between gap-2">
              <span class="border-2 border-black bg-[#00E676] px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">P3</span>
              <span class="font-mono text-[10px] font-bold uppercase text-black/50">SNIP-074</span>
            </div>
            <h4 class="font-archivo text-[14px] font-bold leading-snug line-through decoration-black decoration-2">Write STYLE_TOKENS reference</h4>
            <p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Canonical token doc for all three design styles.</p>
            <div class="mt-2.5 flex items-center justify-between">
              <div class="flex items-center gap-1.5">
                <span class="border border-black bg-white px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">docs</span>
              </div>
              <div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-black font-archivo text-[11px] font-extrabold text-white">SK</div>
            </div>
          </article>
        </div>
        <button type="button" class="nb-add m-3 mt-0 flex w-[calc(100%-1.5rem)] items-center justify-center gap-1.5 border-2 border-black bg-white py-2 font-mono text-[11px] font-bold uppercase shadow-[2px_2px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2">
          <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
          Add card
        </button>
      </section>

    </div>
  </div>

  <style>
    [data-board="nb"] .nb-list { min-height: 56px; transition: background-color .12s ease; }
    [data-board="nb"] .nb-list.nb-over { background-color: rgba(255, 230, 0, 0.35); outline: 2px dashed #000; outline-offset: -2px; }
    [data-board="nb"] .nb-card.nb-drag { opacity: .4; }
    [data-board="nb"] .nb-card.nb-focus:focus { outline: 2px solid #000; outline-offset: 2px; }
    [data-board="nb"] .nb-placeholder { border: 2px dashed #000; background: rgba(0,0,0,0.04); height: 84px; }
    [data-board="nb"] .board-cols { scrollbar-width: thin; scrollbar-color: #000 transparent; }
    [data-board="nb"] .board-cols::-webkit-scrollbar { height: 10px; }
    [data-board="nb"] .board-cols::-webkit-scrollbar-track { background: #FFFDF5; border-top: 2px solid #000; }
    [data-board="nb"] .board-cols::-webkit-scrollbar-thumb { background: #000; }
    [data-board="nb"] .nb-list::-webkit-scrollbar { width: 8px; }
    [data-board="nb"] .nb-list::-webkit-scrollbar-thumb { background: #000; }
  </style>

  <script>
    (function () {
      var root = document.currentScript.closest('[data-board="nb"]');
      if (!root) return;
      var cols = root.querySelectorAll('[data-list]');
      var dragged = null;
      var placeholder = null;
      var uid = 0;

      function updateCounts() {
        root.querySelectorAll('.nb-col').forEach(function (col) {
          var count = col.querySelector('[data-list]').children.length;
          var badge = col.querySelector('[data-count]');
          if (badge) badge.textContent = count;
        });
      }

      function findCard(node) {
        return node && (node.closest ? node.closest('.nb-card') : null);
      }

      function makePlaceholder() {
        var p = document.createElement('div');
        p.className = 'nb-placeholder';
        return p;
      }

      function clearOver() {
        cols.forEach(function (l) { l.classList.remove('nb-over'); });
      }

      function attachCard(card) {
        card.addEventListener('dragstart', function (e) {
          dragged = card;
          card.classList.add('nb-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('nb-drag');
          if (placeholder && placeholder.parentNode) placeholder.parentNode.removeChild(placeholder);
          placeholder = null;
          dragged = null;
          clearOver();
          updateCounts();
        });
      }

      root.querySelectorAll('.nb-card').forEach(attachCard);

      cols.forEach(function (list) {
        list.addEventListener('dragover', function (e) {
          e.preventDefault();
          if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
          list.classList.add('nb-over');
          if (!dragged || !placeholder) return;
          var after = null;
          var cards = list.querySelectorAll('.nb-card:not(.nb-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('nb-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();
        });
      });

      // Add-card affordance
      var palettes = ['#FF4FA3', '#FFE600', '#00C2FF', '#00E676'];
      var avatars = ['AB', 'CD', 'EF', 'GH', 'JK', 'MN'];
      root.querySelectorAll('.nb-add').forEach(function (btn) {
        btn.addEventListener('click', function () {
          var col = btn.closest('.nb-col');
          var list = col.querySelector('[data-list]');
          uid += 1;
          var id = 'SNIP-' + (200 + uid);
          var pri = palettes[uid % palettes.length];
          var dark = pri === '#FFE600' || pri === '#00C2FF' || pri === '#00E676';
          var card = document.createElement('article');
          card.className = 'nb-card cursor-grab border-2 border-black bg-white p-3 shadow-[3px_3px_0_0_#000] transition-transform duration-100 hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-[1px_1px_0_0_#000] 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="border-2 border-black px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase ' + (dark ? 'text-black' : 'text-white') + '" style="background:' + pri + '">P2</span>' +
            '<span class="font-mono text-[10px] font-bold uppercase text-black/50">' + id + '</span>' +
            '</div>' +
            '<h4 class="font-archivo text-[14px] font-bold leading-snug">New task — define the next step</h4>' +
            '<p class="mt-1 font-mono text-[11px] leading-snug text-black/60">Click to flesh out details, owners, and tags.</p>' +
            '<div class="mt-2.5 flex items-center justify-between">' +
            '<div class="flex items-center gap-1.5"><span class="border border-black bg-white px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase">new</span></div>' +
            '<div class="flex h-7 w-7 items-center justify-center border-2 border-black bg-[#FFE600] font-archivo text-[11px] font-extrabold">' + 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('.nb-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('.nb-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>
366 lines UTF-8 · LF · Spaces: 2

AI-ready

Available to your agent.

This section is reachable through the DevSnips CLI, MCP server and Skills integrations — one registry, one resource path.

Resource path

devsnips://sections/app-ui/kanban-board/neo-brutalism

Continue browsing

View all