bêta
Kanban <mv-kanban>
Tableau kanban en couloirs ouverts séparés par des filets fins : glisser-déposer au pointeur avec une copie flottante à plat, un emplacement réservé hachuré et des cartes qui glissent en place (FLIP), un anneau de stabilisation au dépôt, défilement automatique, barres de charge WIP pour les colonnes limitées, et déplacements complets au clavier (Espace, flèches, Échap) annoncés aux lecteurs d’écran.
| Catégorie | Blocs |
|---|---|
| Type | Web Component (<mv-kanban>) |
| Statut | bêta |
| Keywords | kanban, board, drag-and-drop, dnd, sortable, tasks, project |
When to use
- A project tool needs a board where tasks move between columns by drag and drop or keyboard
- Columns have work-in-progress limits that should show a load bar and flag overflow
- Many cards must fit on screen with a compact single-line density or tinted column panels
Avoid when
- Items are records to sort and filter rather than tasks moving through stages → use Data table instead
Installation
node scripts/add.mjs kanban --out ./src/marvelousAgent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["kanban"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, components/kanban/kanban.js, components/kanban/kanban.css.
Utilisation
Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :
<mv-kanban id="kb-demo" label="Sprint 42: Storefront redesign" style="height:640px;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-bg);overflow:hidden">
<style>
#kb-demo .kb-demo-view { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--mv-radius-md); background: var(--mv-bg-muted); }
#kb-demo .kb-demo-view .mv-button[aria-pressed="true"] { background: var(--mv-surface); color: var(--mv-fg); box-shadow: 0 0 0 1px var(--mv-border); }
</style>
<header class="mv-kanban-head">
<div>
<h2>Sprint 42 · Storefront redesign</h2>
<p>Sep 22 → Oct 3 · drag a card, or focus it, press Space and use the arrow keys</p>
</div>
<div class="mv-kanban-head-end">
<div class="kb-demo-view" role="group" aria-label="Board style">
<button class="mv-button" data-variant="ghost" data-size="sm" type="button" aria-pressed="true" data-kb-view="lanes">Lanes</button>
<button class="mv-button" data-variant="ghost" data-size="sm" type="button" aria-pressed="false" data-kb-view="panels">Panels</button>
<button class="mv-button" data-variant="ghost" data-size="sm" type="button" aria-pressed="false" data-kb-view="compact">Compact</button>
</div>
<div class="mv-avatar-group" data-size="sm" role="group" aria-label="Sprint team">
<span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:200" role="img" aria-label="Chloe Ramirez"><span class="mv-avatar-fallback" aria-hidden="true">CR</span></span>
<span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:280" role="img" aria-label="Ivy Morgan"><span class="mv-avatar-fallback" aria-hidden="true">IM</span></span>
<span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:160" role="img" aria-label="Kwame Boateng"><span class="mv-avatar-fallback" aria-hidden="true">KB</span></span>
<span class="mv-avatar-more" aria-label="2 more members">+2</span>
</div>
<button class="mv-button" data-size="sm" type="button" data-kb-demo-add><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>New task</button>
</div>
</header>
<div class="mv-kanban-board">
<section class="mv-kanban-column" data-column="todo" style="--mv-kanban-accent:var(--mv-fg-subtle)">
<header class="mv-kanban-column-header"><h3 class="mv-kanban-column-title">Backlog</h3><span class="mv-kanban-count"></span></header>
<ul class="mv-kanban-list">
<li class="mv-kanban-card" data-id="NOV-151">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="info" data-size="sm">Design</span><span class="mv-kanban-card-id">NOV-151</span></div>
<p class="mv-kanban-card-title">Mobile home screen mockups</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="2" role="img" aria-label="Medium priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Oct 2</span><span aria-label="3 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>3</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:280" role="img" aria-label="Assigned to Ivy Morgan"><span class="mv-avatar-fallback" aria-hidden="true">IM</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-149">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Content</span><span class="mv-kanban-card-id">NOV-149</span></div>
<p class="mv-kanban-card-title">Write the billing FAQ</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="1" role="img" aria-label="Low priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Oct 4</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:240" role="img" aria-label="Assigned to Tom Garcia"><span class="mv-avatar-fallback" aria-hidden="true">TG</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-147">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Dev</span><span class="mv-kanban-card-id">NOV-147</span></div>
<p class="mv-kanban-card-title">Wire up CSV export for orders</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="2" role="img" aria-label="Medium priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Oct 6</span><span aria-label="1 comment"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>1</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:160" role="img" aria-label="Assigned to Kwame Boateng"><span class="mv-avatar-fallback" aria-hidden="true">KB</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-146">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="warning" data-size="sm">Accessibility</span><span class="mv-kanban-card-id">NOV-146</span></div>
<p class="mv-kanban-card-title">Dark mode contrast audit</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="3" role="img" aria-label="High priority"><i></i><i></i><i></i></span><span data-tone="danger"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Sep 30</span><span aria-label="5 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>5</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:320" role="img" aria-label="Assigned to Sofia Nguyen"><span class="mv-avatar-fallback" aria-hidden="true">SN</span></span></div>
</li>
</ul>
</section>
<section class="mv-kanban-column" data-column="doing" data-limit="3" style="--mv-kanban-accent:var(--mv-info)">
<header class="mv-kanban-column-header"><h3 class="mv-kanban-column-title">In progress</h3><span class="mv-kanban-count"></span></header>
<ul class="mv-kanban-list">
<li class="mv-kanban-card" data-id="NOV-140">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Dev</span><span class="mv-kanban-card-id">NOV-140</span></div>
<p class="mv-kanban-card-title">Interest-free installment payments</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="3" role="img" aria-label="High priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Sep 27</span><span aria-label="8 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>8</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:200" role="img" aria-label="Assigned to Chloe Ramirez"><span class="mv-avatar-fallback" aria-hidden="true">CR</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-138">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="info" data-size="sm">Design</span><span class="mv-kanban-card-id">NOV-138</span></div>
<p class="mv-kanban-card-title">Redesign product cards</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="2" role="img" aria-label="Medium priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Oct 1</span><span aria-label="2 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>2</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:280" role="img" aria-label="Assigned to Ivy Morgan"><span class="mv-avatar-fallback" aria-hidden="true">IM</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-135">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Dev</span><span class="mv-kanban-card-id">NOV-135</span></div>
<p class="mv-kanban-card-title">Migrate images to AVIF</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="1" role="img" aria-label="Low priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Oct 3</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:160" role="img" aria-label="Assigned to Kwame Boateng"><span class="mv-avatar-fallback" aria-hidden="true">KB</span></span></div>
</li>
</ul>
</section>
<section class="mv-kanban-column" data-column="review" data-limit="2" style="--mv-kanban-accent:var(--mv-warning)">
<header class="mv-kanban-column-header"><h3 class="mv-kanban-column-title">In review</h3><span class="mv-kanban-count"></span></header>
<ul class="mv-kanban-list">
<li class="mv-kanban-card" data-id="NOV-131">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Dev</span><span class="mv-kanban-card-id">NOV-131</span></div>
<p class="mv-kanban-card-title">Guest checkout flow</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="2" role="img" aria-label="Medium priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Sep 26</span><span aria-label="4 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>4</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:240" role="img" aria-label="Assigned to Tom Garcia"><span class="mv-avatar-fallback" aria-hidden="true">TG</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-129">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Content</span><span class="mv-kanban-card-id">NOV-129</span></div>
<p class="mv-kanban-card-title">German and Italian translations</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="1" role="img" aria-label="Low priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Sep 28</span><span aria-label="1 comment"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>1</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:320" role="img" aria-label="Assigned to Sofia Nguyen"><span class="mv-avatar-fallback" aria-hidden="true">SN</span></span></div>
</li>
</ul>
</section>
<section class="mv-kanban-column" data-column="done" style="--mv-kanban-accent:var(--mv-success)">
<header class="mv-kanban-column-header"><h3 class="mv-kanban-column-title">Done</h3><span class="mv-kanban-count"></span></header>
<ul class="mv-kanban-list">
<li class="mv-kanban-card" data-id="NOV-124">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="info" data-size="sm">Design</span><span class="mv-kanban-card-id">NOV-124</span></div>
<p class="mv-kanban-card-title">New color palette</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="2" role="img" aria-label="Medium priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Sep 19</span><span aria-label="6 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>6</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:280" role="img" aria-label="Assigned to Ivy Morgan"><span class="mv-avatar-fallback" aria-hidden="true">IM</span></span></div>
</li>
<li class="mv-kanban-card" data-id="NOV-120">
<div class="mv-kanban-card-top"><span class="mv-badge" data-variant="secondary" data-size="sm">Dev</span><span class="mv-kanban-card-id">NOV-120</span></div>
<p class="mv-kanban-card-title">Instant catalog search</p>
<div class="mv-kanban-card-meta"><span class="mv-kanban-priority" data-level="3" role="img" aria-label="High priority"><i></i><i></i><i></i></span><span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Sep 17</span><span aria-label="9 comments"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>9</span><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:200" role="img" aria-label="Assigned to Chloe Ramirez"><span class="mv-avatar-fallback" aria-hidden="true">CR</span></span></div>
</li>
</ul>
</section>
</div>
</mv-kanban>
<script type="module">
const board = document.getElementById("kb-demo");
let n = 152;
board?.querySelectorAll("[data-kb-view]").forEach((btn, _, all) => btn.addEventListener("click", () => {
const v = btn.dataset.kbView;
all.forEach((b) => b.setAttribute("aria-pressed", String(b === btn)));
if (v === "panels") board.dataset.variant = "panels"; else delete board.dataset.variant;
if (v === "compact") board.dataset.density = "compact"; else delete board.dataset.density;
}));
board?.querySelector("[data-kb-demo-add]")?.addEventListener("click", () => {
const list = board.querySelector('[data-column="todo"] .mv-kanban-list');
const card = document.createElement("li");
card.className = "mv-kanban-card";
card.dataset.id = `NOV-${n}`;
const top = document.createElement("div");
top.className = "mv-kanban-card-top";
const tag = document.createElement("span");
tag.className = "mv-badge";
tag.dataset.variant = "secondary";
tag.dataset.size = "sm";
tag.textContent = "Triage";
const id = document.createElement("span");
id.className = "mv-kanban-card-id";
id.textContent = `NOV-${n++}`;
top.append(tag, id);
const title = document.createElement("p");
title.className = "mv-kanban-card-title";
title.textContent = "Untitled task";
card.append(top, title);
list.prepend(card);
requestAnimationFrame(() => card.focus());
});
</script>API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
label | string | Board | Accessible name of the region. |
data-variant | panels | On <mv-kanban>: tinted column panels with an accent top rule instead of open lanes (default). | |
data-density | compact | On <mv-kanban>: single-line cards (id, title, assignee); badges and other meta are hidden. | |
data-dragging | boolean | Set during a pointer drag. |
Events
| Name | Description |
|---|---|
mv-change | Card moved (pointer or keyboard). detail: { id, card, from, to, index } (from/to = data-column). |
Content structure
| Name | Description |
|---|---|
.mv-kanban-board | Horizontally scrolling container (snaps in narrow containers). |
section.mv-kanban-column[data-column] | Column; data-limit="3" shows 3/3, draws a WIP load bar under the header (--mv-kanban-load) and sets data-full / data-over; --mv-kanban-accent colors its status ring. |
.mv-kanban-column-header > .mv-kanban-column-title + .mv-kanban-count | Title (names the list) and automatic counter; optional .mv-kanban-column-actions. |
ul.mv-kanban-list > li.mv-kanban-card[data-id] | Focusable cards (cards added later are picked up too). Inner links/buttons don’t start a drag; data-kanban-no-drag excludes other areas. |
CSS classes
| Name | Description |
|---|---|
mv-kanban-head / -head-end | Optional board header, ruled off from the lanes. |
mv-kanban-card-top / -card-id / -card-title / -card-meta | Card parts; the mono id leads the top row, the meta row sits under a dashed rule. |
mv-kanban-priority[data-level=1|2|3] / mv-kanban-progress | Stepped-bar priority gauge, thin progress bar (--value). |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-kanban-column-width | 17.5rem | Column width. |
--mv-kanban-accent | Column color: status ring, WIP bar, drop-target tint, panel top rule. | |
--mv-kanban-load | Set by the element on limited columns: count / limit (0-1). |
Accessibility
Named region; each list is named by its column title; focusable cards with aria-roledescription “draggable card” and instructions (aria-describedby). Keyboard: arrow keys to browse; Space/Enter picks up (dashed outline + “Moving” tag), arrow keys move (↑↓ within the column, ←→ across columns), Space/Enter drops, Escape cancels; every step is announced (aria-live assertive) with column and position; focus follows the card. Counters carry an aria-label (“2 of 3 cards allowed”). Reduced motion: no FLIP, no drop glide, no settle ring.