Columns, cards and drag-and-drop feedback
A good Kanban board lives on clear column boundaries, readable cards and visible feedback while dragging. With Tailwind CSS for layout and states plus Alpine.js for drag-and-drop interaction, a board emerges that stays organized even with many cards and columns.
Table of Contents
- 1. Why a Kanban board needs its own styling rules
- 2. Base structure: column and card layout
- 3. Card design: priority, labels, avatars
- 4. Horizontal scrolling of columns
- 5. Drag-and-drop visual feedback
- 6. Column headers with count and color
- 7. Responsive behavior: mobile alternatives
- 8. Displaying WIP limits visually
- 9. Kanban board vs. list and table view
- 10. Summary
- 11. FAQ
1. Why a Kanban board needs its own styling rules
A Kanban board differs structurally from most other layout patterns, since it manages two axes at once: the horizontal sequence of columns as process steps, and the vertical stacking of cards inside each column. This dual structure brings its own challenges, such as horizontal scrolling with many columns, while every single column must at the same time stay vertically scrollable without scrolling the whole page along with it.
A second difference from classic list layouts: cards in a Kanban board are actively moved between columns, which requires direct visual feedback while dragging. Without that feedback, the board feels technically functional but not tangible. The following sections build a complete Kanban board, from the base structure through card design to WIP limits.
2. Base structure: column and card layout
The base structure of a Kanban board is a flex container with overflow-x-auto, where every column is its own flex item with a fixed minimum width. This fixed width prevents columns from shrinking with few cards and making the board look visually unstable. Inside each column, overflow-y-auto with a maximum height ensures that many cards inside a column scroll without affecting the overall board's height.
For a Kanban board with typically three to six columns, a fixed width of around 300 pixels per column is enough, combined with gap-4 between columns for even spacing. It matters that the column container itself does not receive a fixed height, but adapts to available viewport space, usually through h-[calc(100vh-Xpx)] minus header and navigation.
<!-- Kanban board base structure: horizontal columns, vertical card scroll -->
<div class="flex gap-4 overflow-x-auto pb-4">
<div class="flex w-[300px] flex-shrink-0 flex-col rounded-2xl bg-slate-100 p-3">
<div class="mb-3 flex items-center justify-between px-1">
<h3 class="text-sm font-bold text-slate-700">Backlog</h3>
<span class="rounded-full bg-slate-200 px-2 py-0.5 text-xs font-semibold text-slate-600">8</span>
</div>
<div class="flex flex-col gap-2 overflow-y-auto" style="max-height: calc(100vh - 220px);">
<!-- Cards rendered here -->
</div>
</div>
<div class="flex w-[300px] flex-shrink-0 flex-col rounded-2xl bg-slate-100 p-3">
<!-- Next column -->
</div>
</div>
3. Card design: priority, labels, avatars
A card in a Kanban board has to carry several layers of information at once in a small space: title, priority, thematic labels and the assigned person. A proven hierarchy starts with a colored priority indicator as a thin bar on the left card edge, followed by the title in bold, labels below as small colored pills, and an avatar for the assigned person at the bottom edge of the card.
This layering ensures that users perceive the most important information first when quickly scanning the Kanban board, the priority, without needing to read the title. Cards should also get a subtle shadow and a visible hover state, signaling that the card is interactive, for example through a slightly deeper shadow when the mouse moves over it.
<!-- Kanban card with priority indicator, labels and assignee avatar -->
<div
draggable="true"
class="group relative cursor-grab rounded-xl border border-slate-200 bg-white p-3 shadow-sm hover:shadow-md active:cursor-grabbing"
>
<div class="absolute inset-y-0 left-0 w-1 rounded-l-xl bg-red-500"></div>
<p class="mb-2 pl-2 text-sm font-semibold text-slate-800">Fix checkout bug in Safari</p>
<div class="mb-3 flex flex-wrap gap-1.5 pl-2">
<span class="rounded-full bg-red-100 px-2 py-0.5 text-xs font-semibold text-red-700">Bug</span>
<span class="rounded-full bg-sky-100 px-2 py-0.5 text-xs font-semibold text-sky-700">Frontend</span>
</div>
<div class="flex items-center justify-between pl-2">
<span class="text-xs text-slate-400">#284</span>
<img class="h-6 w-6 rounded-full ring-2 ring-white" src="/media/avatars/jd.webp" alt="Assigned to Julia Decker">
</div>
</div>
4. Horizontal scrolling of columns
As soon as a Kanban board contains more columns than fit in the visible viewport, horizontal scrolling has to work reliably without affecting the vertical page scrollbar. The outer column container needs overflow-x-auto combined with flex for that, while the parent page container itself must not set a horizontal overflow constraint, since that would create two competing scroll areas.
A detail many Kanban board implementations overlook: a visible but unobtrusive scrollbar styling improves the user experience considerably. With Tailwind utilities for scrollbar-thin, complemented by an official scrollbar plugin or custom ::-webkit-scrollbar rules, the browser's default scrollbar can be visually matched to the rest of the design instead of simply being hidden and taking away orientation from users.
/* Subtle scrollbar styling for the horizontal board container */
.kanban-board::-webkit-scrollbar {
height: 8px;
}
.kanban-board::-webkit-scrollbar-track {
background: theme(colors.slate.100);
border-radius: 9999px;
}
.kanban-board::-webkit-scrollbar-thumb {
background: theme(colors.slate.300);
border-radius: 9999px;
}
.kanban-board::-webkit-scrollbar-thumb:hover {
background: theme(colors.slate.400);
}
/* Column body: vertical scroll, no visible scrollbar bleed into layout */
.kanban-column-body {
overflow-y: auto;
overscroll-behavior: contain;
}
5. Drag-and-drop visual feedback
The native HTML5 drag-and-drop API delivers the events dragstart, dragover, drop and dragend, which can be wired directly to Alpine.js directives without needing an additional library. For a convincing Kanban board, technical functionality alone is not enough though, what matters is the visual feedback throughout the entire operation: the dragged card should become more transparent, and the target column should get a visible border or background as soon as a card hovers over it.
A clean pattern in a Kanban board is showing a placeholder row at the likely insertion position during dragover, instead of letting the card only jump on the actual drop. This significantly reduces the cognitive load for users, since the final position already becomes visible while dragging, not only afterward.
// Alpine.js component wiring native HTML5 drag-and-drop events
function kanbanColumn() {
return {
isDragOver: false,
onDragStart(event, cardId) {
event.dataTransfer.setData('text/plain', cardId);
event.dataTransfer.effectAllowed = 'move';
// Slight delay so the browser can render the drag ghost first
setTimeout(() => event.target.classList.add('opacity-40'), 0);
},
onDragEnd(event) {
event.target.classList.remove('opacity-40');
},
onDragOver(event) {
event.preventDefault();
this.isDragOver = true;
},
onDragLeave() {
this.isDragOver = false;
},
onDrop(event, targetColumnId) {
event.preventDefault();
this.isDragOver = false;
const cardId = event.dataTransfer.getData('text/plain');
this.$dispatch('card-moved', { cardId, targetColumnId });
}
};
}
6. Column headers with count and color
The column header in a Kanban board carries three pieces of information at once: the name of the process step, the number of cards it contains, and optionally a color coding that shows whether the column represents a starting, middle or final state. A pale gray for backlog, a neutral blue for in progress and a rich green for done are a proven, immediately understandable convention.
The count in a Kanban board header should update automatically as soon as a card is moved into or out of the column through drag-and-drop. This reactivity can be solved elegantly in Alpine.js through a computed property that simply returns the length of the respective column's card array, instead of manually incrementing or decrementing the count on every drop operation.
7. Responsive behavior: mobile alternatives
A horizontally scrolling Kanban board works well on desktop screens, but quickly becomes unwieldy on small mobile devices, especially combined with drag-and-drop, which behaves differently on touch devices than with a mouse anyway. A proven solution is switching to a single column view with a column switcher below a certain breakpoint, where only one column is visible at a time.
For moving cards on touch devices, a simpler pattern than complex drag-and-drop is recommended: tapping the card opens a context menu with the available target columns as a list. This pattern is more reliable on a touchscreen than a drag gesture, which easily collides with vertically scrolling the page, and keeps the Kanban board fully operable even without a mouse.
8. Displaying WIP limits visually
A WIP limit, short for work in progress, restricts the number of cards allowed at the same time inside a column of a Kanban board, to make bottlenecks in the process visible. Visually this limit is most effectively displayed directly in the column header, for example as a fraction like 5 of 3, with the number switching to red once exceeded, instead of staying in the neutral default color.
In addition to the number display, a colored border around the entire column reinforces the signal as soon as the WIP limit is exceeded. This double encoding through number and border color ensures the violation stands out even when quickly scanning the Kanban board, without users needing to read the exact count value.
<!-- WIP limit indicator: red border and count when the limit is exceeded -->
<div
class="flex w-[300px] flex-shrink-0 flex-col rounded-2xl p-3"
:class="cards.length > wipLimit ? 'bg-red-50 ring-2 ring-red-300' : 'bg-slate-100'"
>
<div class="mb-3 flex items-center justify-between px-1">
<h3 class="text-sm font-bold text-slate-700">In progress</h3>
<span
class="rounded-full px-2 py-0.5 text-xs font-semibold"
:class="cards.length > wipLimit ? 'bg-red-200 text-red-800' : 'bg-slate-200 text-slate-600'"
x-text="`${cards.length} / ${wipLimit}`"
></span>
</div>
</div>
9. Kanban board vs. list and table view
Not every task benefits from a Kanban board as a visual representation. For some data volumes and usage scenarios, a plain list or a sortable table is the better choice. The following table compares the three patterns against concrete criteria.
| Criterion | Kanban board | List | Table |
|---|---|---|---|
| Process visualization | Very good | Weak | Moderate |
| Many data fields per entry | Limited by card size | Moderate | Very good |
| Sorting and filtering | Cumbersome | Good | Very good |
| Status change via drag-and-drop | Native fit | Uncommon | Uncommon |
A Kanban board suits processes with clearly defined states and a manageable number of fields per entry best, such as task management or support tickets. Once users regularly need to filter or sort by many different criteria, a table usually becomes more efficient, while a plain list is enough for very linear, unstructured tasks.
Mironsoft
Tailwind CSS components and design systems
A Kanban board that actually feels smooth?
We build Kanban boards with Tailwind CSS and Alpine.js, with clean drag-and-drop feedback, WIP limits and a mobile alternative that works without a mouse too.
Board concept
Defining columns, card layout and color system for your process
Drag-and-drop
Wiring the native HTML5 API with Alpine.js, including placeholder feedback
Mobile adaptation
Implementing a touch-friendly alternative without a drag gesture
10. Summary
A well thought through Kanban board manages two layout axes at once, horizontal scrolling between columns and vertical scrolling inside each column, without the two movements interfering with each other. Card design with a clear priority, label and avatar hierarchy makes the board readable at a glance, while native HTML5 drag-and-drop events combined with Alpine.js provide smooth visual feedback.
WIP limits with double encoding through number and border color make bottlenecks immediately visible, and a deliberate mobile alternative without a complex drag gesture keeps the Kanban board fully operable on touch devices too. Anyone applying these patterns consistently ends up with a board that is not just technically functional, but feels tangible to users too.
Kanban Board Styling Patterns — Key Takeaways
Layout
Fixed column width, overflow-x-auto for the container, overflow-y-auto per column.
Cards
Priority bar, title, labels as pills, avatar at the bottom right as a fixed hierarchy.
Drag-and-drop
Native HTML5 events plus Alpine.js for transparency and target column highlighting.
WIP limits & mobile
Number plus border color when exceeded, single column view with a tap menu on touch devices.