# PACKAGE: @nofinite/kanban
Interactive accessible drag-and-drop Kanban workflow boards with optimistic reordering and swimlane support.
You are an expert React developer using @nofinite/kanban.
Rules:
1. Import Kanban from '@nofinite/kanban/react'.
2. Accepts columns with items and onCardMove / onColumnMove callbacks.
3. Full keyboard accessibility and touch support built-in.
---
## TypeScript API Reference (Props)
### Kanban
- `columns (required): KanbanColumnProps[]` (Default: none)
- `onChange: ((columns: KanbanColumnProps[]) => void)` (Default: none)
- `onCardMove: ((event: CardMoveEvent) => void)` (Default: none)
- `onColumnMove: ((event: ColumnMoveEvent) => void)` (Default: none)
- `onCardAdd: ((columnId: string) => void)` (Default: none)
- `onCardClick: ((cardId: string, columnId: string, item: KanbanItem) => void)` (Default: none)
- `onColumnOptions: ((columnId: string) => void)` (Default: none)
- `onColumnAdd: (() => void)` (Default: none)
- `onColumnClear: ((columnId: string) => void)` (Default: none)
- `allowAdd: boolean` (Default: true)
- `allowAddColumn: boolean` (Default: false)
- `addColumnLabel: string` (Default: + Add Column)
- `headerActions: boolean` (Default: true)
- `enforceWipLimit: boolean` (Default: false)
- `canMoveCard: ((event: CardMoveEvent) => boolean)` (Default: none)
- `columnClassName: string` (Default: none)
- `renderCard: ((item: KanbanItem, columnId: string) => ReactNode)` (Default: none)
- `renderHeader: ((column: KanbanColumnProps) => ReactNode)` (Default: none)
---
### Document: kanban/board/page.mdx
# Board & Columns Architecture
Understand the underlying data model, event flows, and spatial drag mechanics of **Nofinite Kanban**.
```ts
// Framework-Agnostic Core
import {
createKanbanBoard,
type KanbanColumn,
type KanbanItem,
type CardMoveEvent,
type ColumnMoveEvent
} from '@nofinite/kanban';
// React Adapter
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
```
---
## Data Model
The data structures are pure, framework-agnostic TypeScript interfaces shared across Vanilla JS, Vue, Svelte, and React:
### `KanbanColumn`
Represents a workflow column / stage:
```typescript
export interface KanbanColumn {
id: string; // Unique column identifier
title?: string; // Human-readable header title
wipLimit?: number; // Maximum recommended card capacity
items: KanbanItem[]; // Array of card items in this column
allowAdd?: boolean; // Renders "+ Add Task" button
headerActions?: boolean; // Renders column action ellipsis menu
}
```
### `KanbanItem`
Represents a task card:
```typescript
export interface KanbanItem {
id: string; // Unique card identifier
title?: string; // Primary card title
description?: string; // Secondary card summary
badge?: { // Priority or status badge
text: string;
variant?: 'default' | 'primary' | 'success' | 'warning' | 'danger';
};
assignee?: { // Assigned team member
name: string;
avatarUrl?: string;
initials?: string;
};
dueDate?: string; // Target deadline date string
isOverdue?: boolean; // Overdue visual state flag
subtasks?: { // Subtask count progress
completed: number;
total: number;
};
}
```
---
## 1. Framework-Agnostic Core Event Flow
When using the core DOM engine, pass callbacks to intercept lifecycle and drag actions:
```ts
import { createKanbanBoard } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
const board = createKanbanBoard(document.getElementById('board'), {
columns: initialColumns,
enforceWipLimit: true,
onCardMove: (event: CardMoveEvent) => {
console.log(`Card ${event.cardId} moved from ${event.sourceColumnId} (index ${event.sourceIndex}) to ${event.targetColumnId} (index ${event.targetIndex})`);
},
onColumnMove: (event: ColumnMoveEvent) => {
console.log(`Column ${event.columnId} moved from index ${event.sourceIndex} to ${event.targetIndex}`);
},
onCardClick: (item, columnId) => {
console.log('Opened task detail drawer:', item.id);
}
});
```
---
## 2. React Adapter Integration
In React applications, bind standard React state to ``:
```tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
export function SprintBoard() {
const [columns, setColumns] = useState(initialColumns);
return (
console.log('Card moved:', event)}
onColumnMove={(event) => console.log('Column moved:', event)}
enforceWipLimit={true}
/>
);
}
```
---
### Document: kanban/customization/page.mdx
# Kanban Customization and Slots
**Nofinite Kanban** provides a flexible customization architecture allowing teams to customize card presentation, column headers, metadata badges, user avatars, and subtask progress indicators.
By default, cards render an accessible, high-density layout styled with **NUICSS** superclasses. You can replace default card bodies or column headers without breaking Pragmatic Drag-and-Drop hitboxes or keyboard navigation.
```ts
// Framework-Agnostic Core
import { createKanbanBoard, renderCardHTML } from '@nofinite/kanban';
// React Adapter
import { Kanban } from '@nofinite/kanban/react';
```
---
## 1. Framework-Agnostic Core Customization
In Vanilla JavaScript, TypeScript, or non-React web components, pass a custom `renderCardHTML` callback when creating the board:
```ts
import { createKanbanBoard } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
const board = createKanbanBoard(document.getElementById('board'), {
columns: myColumns,
// Custom HTML template for each task card
renderCardHTML: (item, col, itemIndex, colIndex) => {
return `
${item.title}
${item.description ? `${item.description}
` : ''}
`.trim();
}
});
```
---
## 2. React Slot-Based Customization
### Custom Card Slot (`renderCard`)
In React, use `renderCard` to render any custom JSX component inside the draggable card container:
```tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps, type KanbanItem } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
interface SprintTask extends KanbanItem {
storyPoints?: number;
priority?: 'critical' | 'high' | 'medium' | 'low';
}
export function CustomCardBoard() {
const [columns, setColumns] = useState(/* ... */);
return (
{/* Title & Description */}
)}
/>
);
}
```
### Custom Column Header Slot (`renderHeader`)
Customize column headers to add custom metrics or quick action buttons:
```tsx
(
)}
/>
```
---
### Document: kanban/examples/page.mdx
# Kanban Production Examples
Below are production-ready, copy-paste implementations of **Nofinite Kanban** showcasing both **Framework-Agnostic Core** and **React** integrations.
---
## Example 1: Vanilla TypeScript Real-Time Kanban Board
Direct DOM mounting with real-time external card additions, search filtering, and state event handling without React:
```ts
import { createKanbanBoard, type KanbanColumn, type CardMoveEvent } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
// 1. Initial sprint column data
const sprintColumns: KanbanColumn[] = [
{
id: 'backlog',
title: 'Sprint Backlog',
items: [
{
id: 'eng-101',
title: 'Upgrade Node.js to v22 LTS in Docker base image',
badge: { text: 'DevOps', variant: 'primary' },
assignee: { name: 'Alex Rivera' },
subtasks: { total: 3, completed: 0 },
dueDate: 'Oct 20'
}
]
},
{
id: 'in-progress',
title: 'In Progress',
wipLimit: 2, // Hard limit of 2 active cards
items: [
{
id: 'eng-103',
title: 'Optimize SVG path calculation for multi-series charts',
badge: { text: 'Performance', variant: 'warning' },
assignee: { name: 'Marcus Vance' },
subtasks: { total: 4, completed: 3 },
dueDate: 'Tomorrow'
}
]
},
{
id: 'done',
title: 'Completed',
items: []
}
];
// 2. Mount board directly into container
const container = document.getElementById('kanban-app')!;
const board = createKanbanBoard(container, {
columns: sprintColumns,
enforceWipLimit: true,
onCardMove: (event: CardMoveEvent) => {
console.log(`[Telemetry] Moved card ${event.cardId} to column ${event.targetColumnId}`);
// Sync with backend API:
// fetch('/api/tasks/move', { method: 'POST', body: JSON.stringify(event) });
}
});
// 3. Connect external search input
document.getElementById('task-search')?.addEventListener('input', (e) => {
const query = (e.target as HTMLInputElement).value;
board.setFilter({ query });
});
// 4. Connect external Quick Add button
document.getElementById('quick-add-btn')?.addEventListener('click', () => {
board.addCard('backlog', {
id: `eng-${Date.now()}`,
title: 'New Incoming Ticket',
badge: { text: 'Triage', variant: 'default' }
});
});
```
---
## Example 2: React Agile Engineering Sprint Board
Features strict Work-in-Progress (WIP) limits, priority badges, story point counters, assignee avatars, and interactive subtasks:
```tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
const sprintColumns: KanbanColumnProps[] = [
{
id: 'backlog',
title: 'Sprint Backlog',
items: [
{
id: 'eng-101',
title: 'Upgrade Node.js to v22 LTS in Docker base image',
badge: { text: 'DevOps', variant: 'primary' },
assignee: {
name: 'Alex Rivera',
avatarUrl: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100',
},
subtasks: { total: 3, completed: 0 },
dueDate: 'Oct 20',
},
],
},
{
id: 'in-progress',
title: 'In Progress',
wipLimit: 2, // Hard limit of 2 active cards
items: [
{
id: 'eng-103',
title: 'Optimize SVG path calculation for multi-series charts',
badge: { text: 'Performance', variant: 'warning' },
assignee: {
name: 'Marcus Vance',
avatarUrl: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',
},
subtasks: { total: 4, completed: 3 },
dueDate: 'Tomorrow',
},
],
},
{
id: 'done',
title: 'Completed',
items: [],
},
];
export function AgileSprintBoard() {
const [columns, setColumns] = useState(sprintColumns);
return (
);
}
```
---
### Document: kanban/features/page.mdx
# WIP Limits, Filtering & Keyboard Accessibility
Deep-dive into advanced productivity features in **Nofinite Kanban**, available across both the framework-agnostic DOM core and the React adapter.
```ts
// Framework-Agnostic Core
import { createKanbanBoard } from '@nofinite/kanban';
// React Adapter
import { Kanban } from '@nofinite/kanban/react';
```
---
## 1. Framework-Agnostic Core Features
### Enforceable WIP Limits
Enforce column capacity constraints and hard reject invalid drag drops:
```ts
import { createKanbanBoard } from '@nofinite/kanban';
const board = createKanbanBoard(document.getElementById('board'), {
columns: [
{
id: 'in-review',
title: 'In Review',
wipLimit: 3, // Capacity limit of 3 cards
items: [/* ... */]
}
],
enforceWipLimit: true // Rejects drop and animates card back to source if target column exceeds capacity
});
```
### Real-Time Filtering (`setFilter`)
Filter tasks programmatically without losing drag-and-drop hitboxes or reordering state:
```ts
// Filter by search query, assignee, or tag
board.setFilter({
query: 'Authentication',
assignee: 'Sarah Connor',
badge: 'Critical'
});
// Clear filters
board.setFilter({});
```
### Keyboard Navigation & WAI-ARIA
The board includes built-in keyboard navigation conforming to W3C standards:
- Tab: Navigate through columns and cards.
- Space / Enter: Grab or drop active card.
- Arrow Left / Arrow Right: Move grabbed card between columns.
- Arrow Up / Arrow Down: Reorder grabbed card within column.
- Escape: Cancel active drag and restore original position.
---
## 2. React Adapter Usage
In React applications, bind filtering and WIP bounds via component props:
```tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
export function ManagedBoard() {
const [columns, setColumns] = useState(/* ... */);
const [search, setSearch] = useState('');
return (
);
}
```
---
### Document: kanban/imperative-api/page.mdx
# Kanban Imperative API (`KanbanBoardInstance` & `KanbanHandle`)
While **Nofinite Kanban** provides declarative bindings, complex platforms frequently require **imperative programmatic control** (e.g. automated websocket updates, global command palettes, shortcut keys, or batch database operations).
Every method available through the React `KanbanHandle` is exposed directly by the **Framework-Agnostic Core instance**.
```ts
// Framework-Agnostic Core
import { createKanbanBoard, type KanbanBoardInstance } from '@nofinite/kanban';
// React Adapter
import { Kanban, type KanbanHandle } from '@nofinite/kanban/react';
```
---
## 1. Framework-Agnostic Core Imperative Control
When instantiating the board via `createKanbanBoard()`, you receive a `KanbanBoardInstance` object exposing 12 programmatic control methods:
```ts
import { createKanbanBoard } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
const board = createKanbanBoard(document.getElementById('board'), {
columns: initialColumns
});
// 1. Add a card programmatically
board.addCard('backlog', {
id: 'task-101',
title: 'Automated CI Pipeline Failure',
badge: { text: 'Critical', variant: 'danger' }
});
// 2. Move a card between columns
board.moveCard({
cardId: 'task-101',
sourceColumnId: 'backlog',
targetColumnId: 'in-progress',
sourceIndex: 0,
targetIndex: 0
});
// 3. Update task metadata
board.updateCard('in-progress', 'task-101', {
title: 'CI Pipeline (Resolved)'
});
// 4. Focus card for keyboard accessibility
board.focusCard('task-101');
// 5. Update filter search
board.setFilter({ query: 'Pipeline' });
// 6. Clean up board
board.destroy();
```
---
## 2. React Adapter Imperative Control (`KanbanHandle`)
In React, attach a React `useRef` typed as `KanbanHandle`:
```tsx
import React, { useRef } from 'react';
import { Kanban, type KanbanHandle, type KanbanColumnProps } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
export function ControlledKanban() {
const kanbanRef = useRef(null);
const handleExternalAction = () => {
kanbanRef.current?.addCard('backlog', {
id: `task-${Date.now()}`,
title: 'New Automated Story',
});
};
return (
);
}
```
---
## Method Reference
| Method | Signature | Description |
| :--- | :--- | :--- |
| `addCard` | `(columnId: string, item: KanbanItem) => void` | Appends a new task card to the specified column |
| `updateCard` | `(columnId: string, cardId: string, partial: Partial) => void` | Updates specific properties (title, assignee, badge) on a card |
| `removeCard` | `(columnId: string, cardId: string) => void` | Deletes a task card from the specified column |
| `moveCard` | `(event: CardMoveEvent) => boolean` | Programmatically moves a card between or within columns |
| `focusCard` | `(cardId: string) => void` | Scrolls to and focuses a specific card for keyboard navigation |
| `addColumn` | `(column: KanbanColumn) => void` | Appends a new column to the board |
| `removeColumn` | `(columnId: string) => void` | Removes a column and all its child cards |
| `clearColumn` | `(columnId: string) => void` | Removes all cards from a column while preserving the column itself |
| `setFilter` | `(filter: KanbanFilterOptions) => void` | Programmatically updates the active search query or filter tags |
| `getFilter` | `() => KanbanFilterOptions` | Returns the current active filter object |
| `getColumns` | `() => KanbanColumn[]` | Returns a deep snapshot of current column and card data |
| `setColumns` | `(columns: KanbanColumn[]) => void` | Replaces the entire board column dataset |
| `destroy` | `() => void` | Unbinds all Pragmatic Drag-and-Drop listeners and cleans DOM |
---
### Document: kanban/installation/page.mdx
# Installation & Setup
Get started with `@nofinite/kanban`.
**Nofinite Kanban** is engineered as a **universal, framework-agnostic** drag-and-drop system. The core engine (`@nofinite/kanban`) mounts directly into any DOM element across Vanilla TypeScript, Vue, Svelte, or Angular with zero framework overhead. The React adapter (`@nofinite/kanban/react`) provides declarative JSX components for React 18 & 19.
---
## 1. Package Installation
Install `@nofinite/kanban` alongside `@nofinite/nuicss` using your preferred package manager:
```bash
# pnpm
pnpm add @nofinite/kanban @nofinite/nuicss
# npm
npm install @nofinite/kanban @nofinite/nuicss
# yarn
yarn add @nofinite/kanban @nofinite/nuicss
```
> **Optional Peer Dependency:** `react` and `react-dom` are strictly **optional** peer dependencies (`peerDependenciesMeta.react.optional: true`). If building with Vanilla JS, Svelte, or Vue, you do not need to install React.
---
## 2. Stylesheet Configuration
Board flex geometry, card dragging states, drop hitboxes, and WIP limit badges are bundled directly into **NUICSS**.
Import the single unified stylesheet at the root of your application (`globals.css` or `main.ts`):
```css
/* In your CSS */
@import "@nofinite/nuicss";
```
Or in your JavaScript/TypeScript entry point:
```ts
import '@nofinite/nuicss/styles.css';
```
> **Zero Downstream CSS Invariant:** Do **not** import `@nofinite/kanban/styles.css`. NUICSS provides all necessary semantic superclasses (`.kanban-board`, `.kanban-column`, `.kanban-card`) automatically.
---
## 3. Verification
### Option A: Framework-Agnostic Core Verification (Vanilla JS / TS / DOM)
Verify that the board mounts directly into a raw HTML container:
```html
```
### Option B: React Verification (`@nofinite/kanban/react`)
If developing in React or Next.js, verify the declarative component:
```tsx
'use client';
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
export default function TestBoard() {
const [columns, setColumns] = useState([
{ id: 'todo', title: 'To Do', items: [{ id: '1', title: 'First Task' }] },
{ id: 'done', title: 'Done', items: [] }
]);
return (
);
}
```
---
### Document: kanban/llms/page.mdx
# LLMs.txt Documentation
How to get tools like Cursor, Windsurf, GitHub Copilot, ChatGPT, and Claude to understand `@nofinite/kanban`.
> [!TIP]
> Looking for unified ecosystem presets or multi-package rules? Visit our central [AI Agents Hub](/ai).
We support **LLMs.txt** files for making `@nofinite/kanban` workflow boards directly available to large language models.
---
## Directory Overview
The following machine-readable context files are available:
- [`/kanban/llms.txt`](/kanban/llms.txt): The main LLMs index for `@nofinite/kanban`.
- [`/kanban/llms-full.txt`](/kanban/llms-full.txt): Complete context bundle containing board architecture, WIP limits, and callbacks.
- [`/llms.txt`](/llms.txt): Ecosystem index covering all 8 `@nofinite` libraries.
- [`/llms-full.txt`](/llms-full.txt): The unified ecosystem bundle.
---
## Usage with Cursor & Windsurf
```markdown
# Kanban Rules for AI Assistants
1. Component Imports: Import Kanban from '@nofinite/kanban/react'.
2. Board Structure: Supply columns with id, title, wipLimit, and items array.
3. Event Handlers: Handle card moves with onCardMove callback or manage state with onChange.
```
---
### Document: kanban/page.mdx
---
## Interactive Preview
console.log('Card moved:', event)
});`}
/>
---
## Quickstart
### 1. Framework-Agnostic Core (`@nofinite/kanban`)
Mount an interactive drag-and-drop Kanban board directly into any HTML DOM element. Works in **Vanilla JavaScript**, **TypeScript**, **Vue 3**, **Svelte 5**, **SolidJS**, or **Angular**:
```ts
import { createKanbanBoard, type KanbanColumn } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
const container = document.getElementById('board')!;
const initialColumns: KanbanColumn[] = [
{
id: 'todo',
title: 'To Do',
wipLimit: 5,
items: [
{ id: '1', title: 'Design System Audit', badge: { text: 'Design', variant: 'primary' } },
{ id: '2', title: 'Write Playwright Tests', badge: { text: 'QA', variant: 'success' } }
]
},
{
id: 'doing',
title: 'In Progress',
wipLimit: 3,
items: [
{ id: '3', title: 'Turbopack Optimization', badge: { text: 'Core', variant: 'warning' } }
]
},
{
id: 'done',
title: 'Done',
items: []
}
];
// Mount stateful board instance
const board = createKanbanBoard(container, {
columns: initialColumns,
enforceWipLimit: true,
onCardMove: (event) => console.log('Card moved:', event),
onColumnMove: (event) => console.log('Column reordered:', event)
});
// Imperative methods on the board instance:
// board.addCard('todo', { id: '4', title: 'New Task' });
// board.setFilter({ query: 'Audit' });
// board.destroy();
```
### 2. React Adapter (`@nofinite/kanban/react`)
Declarative React JSX component for React 18 & 19:
```tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
export default function ProjectBoard() {
const [columns, setColumns] = useState([
{
id: 'todo',
title: 'To Do',
wipLimit: 5,
items: [
{ id: '1', title: 'Design System Audit', badge: { text: 'Design', variant: 'primary' } },
{ id: '2', title: 'Write Documentation', badge: { text: 'Docs', variant: 'success' } }
]
},
{
id: 'doing',
title: 'In Progress',
wipLimit: 3,
items: [
{ id: '3', title: 'Turbopack Upgrade', badge: { text: 'Core', variant: 'warning' } }
]
},
{
id: 'done',
title: 'Done',
items: []
}
]);
return (
);
}
```
---
## Core Architectural Invariants
- **Universal Framework-Agnostic Core:** Zero React dependency required. The root export (`@nofinite/kanban`) provides `createKanbanBoard()`, `KanbanStore`, and drag hitboxes for vanilla DOM, Vue, and Svelte applications. React (`@nofinite/kanban/react`) is an optional adapter.
- **Powered by Pragmatic Drag-and-Drop:** Built on pointer events and Atlassian's Pragmatic Drag-and-Drop primitives rather than legacy virtual DOM drag libraries. Offers zero rendering lag, silky 60fps animations, and a tiny bundle footprint.
- **Unified NUICSS Engine (Zero Separate CSS):** All board flex containers, card drop indicators, and dragging states are bundled directly into **NUICSS**. You only ever need a single stylesheet import (`import '@nofinite/nuicss/styles.css'`).
---
## Key Capabilities
- **Slot-Based Customization:** Completely customize card UI with `renderCard(item, columnId)` and column headers with `renderHeader(column)`.
- **Enforceable WIP Limits:** Set column capacity constraints (`wipLimit: 4`). When `enforceWipLimit={true}`, overflows are hard rejected with smooth snap-back animations.
- **Business Logic Move Validation:** Intercept drag-and-drop actions with `canMoveCard={(event) => boolean}` to enforce state machines and role permissions.
- **12 Imperative Methods:** Programmatic board control (`moveCard`, `addCard`, `updateCard`, `focusCard`, `setFilter`) available on both the Vanilla instance and React `ref`.
- **Built-in Search & Filter Engine:** Filter cards across all columns by text query (`query`), assignee (`assignee`), or tag (`badge`).
- **WAI-ARIA Keyboard Accessibility:** Full keyboard reordering via Space and Arrow Keys with live status announcements.
---
## Documentation Directory
### Advanced Architecture & Deep Guides
- [Vanilla Core (`createKanbanBoard`)](/kanban/vanilla-core) — Using the framework-agnostic core in vanilla JavaScript, Vue, Svelte, or Angular without React.
- [Card & Header Customization](/kanban/customization) — Custom card rendering slots (`renderCard`), column header slots (`renderHeader`), badges, assignees, and subtasks.
- [Permissions & Move Validation](/kanban/permissions-and-validation) — WIP limits enforcement, state machine validation via `canMoveCard`, and role-based permissions.
- [Imperative API (`KanbanHandle`)](/kanban/imperative-api) — Programmatic board control (`moveCard`, `addCard`, `updateCard`, `focusCard`, `setFilter`).
- [Production Examples](/kanban/examples) — Complete copy-paste examples: Agile Sprint Board, Support Ticket Triage, and Multi-Filter Search Board.
### Primitives & Setup
- [Installation & Setup](/kanban/installation) — Package installation, peer dependencies, and styling configuration.
- [Board Architecture & Events](/kanban/board) — Column & card data model and drag event listeners.
- [WIP Limits & Filters](/kanban/features) — Capacity bounds, search filters, and keyboard navigation.
---
### Document: kanban/permissions-and-validation/page.mdx
# Permissions & Move Validation
**Nofinite Kanban** provides robust validation hooks allowing teams to intercept drag-and-drop actions, enforce state machine transitions, and apply role-based permission checks before any state update commits.
When a validation hook returns `false`, the dragged card snaps smoothly back to its origin without DOM mutation or data corruption.
```ts
// Framework-Agnostic Core
import { createKanbanBoard, type CardMoveEvent } from '@nofinite/kanban';
// React Adapter
import { Kanban } from '@nofinite/kanban/react';
```
---
## 1. Framework-Agnostic Core Validation
Pass `canMoveCard` to `createKanbanBoard()` to validate card transitions:
```ts
import { createKanbanBoard, type CardMoveEvent } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
const board = createKanbanBoard(document.getElementById('board'), {
columns: initialColumns,
enforceWipLimit: true,
// Intercept and evaluate card move legality
canMoveCard: (event: CardMoveEvent): boolean => {
const { cardId, sourceColumnId, targetColumnId } = event;
// Rule 1: Cannot skip from 'Backlog' directly to 'Done'
if (sourceColumnId === 'backlog' && targetColumnId === 'done') {
console.warn('Forbidden transition: tasks must pass through In Progress.');
return false; // Automatically triggers smooth snap-back animation
}
// Rule 2: Role-based permissions
const currentUserRole = 'engineer'; // From session
if (targetColumnId === 'production' && currentUserRole !== 'admin') {
alert('Only administrators can promote tasks to Production.');
return false;
}
return true; // Move permitted
}
});
```
---
## 2. React Adapter Validation
In React applications, provide `canMoveCard` and `canMoveColumn` as declarative component props:
```tsx
import React, { useState } from 'react';
import { Kanban, type KanbanColumnProps, type CardMoveEvent } from '@nofinite/kanban/react';
import '@nofinite/nuicss/styles.css';
export function ValidatedWorkflowBoard({ userRole }: { userRole: string }) {
const [columns, setColumns] = useState(/* ... */);
const validateCardMove = (event: CardMoveEvent): boolean => {
// Prevent non-QA members from moving cards to 'Verified'
if (event.targetColumnId === 'verified' && userRole !== 'qa') {
return false;
}
return true;
};
return (
);
}
```
---
### Document: kanban/vanilla-core/page.mdx
# Framework-Agnostic Core (`createKanbanBoard`)
Under the hood, **Nofinite Kanban** is built upon a framework-agnostic, zero-React core: `createKanbanBoard`.
The vanilla core mounts directly into any standard DOM container in **Vanilla TypeScript**, **Vue 3**, **Svelte 5**, **SolidJS**, or **Angular**, while consuming identical **NUICSS** superclasses.
```ts
import {
createKanbanBoard,
type KanbanColumn,
type KanbanItem,
type KanbanBoardInstance
} from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
```
---
## 1. Quickstart (Vanilla TypeScript / DOM)
```ts
import { createKanbanBoard, type KanbanColumn } from '@nofinite/kanban';
import '@nofinite/nuicss/styles.css';
const container = document.getElementById('kanban-root')!;
const initialColumns: KanbanColumn[] = [
{
id: 'todo',
title: 'To Do',
wipLimit: 5,
items: [
{ id: 'task-1', title: 'Implement Auth Tokens', badge: { text: 'Security', variant: 'danger' } },
{ id: 'task-2', title: 'Write Playwright Tests', badge: { text: 'QA', variant: 'success' } },
],
},
{
id: 'in-progress',
title: 'In Progress',
wipLimit: 2,
items: [{ id: 'task-3', title: 'Optimize Bundle Sizing' }],
},
{
id: 'done',
title: 'Done',
items: [],
},
];
const board: KanbanBoardInstance = createKanbanBoard(container, {
columns: initialColumns,
enforceWipLimit: true,
onCardMove: (event) => console.log('Card moved:', event),
onColumnMove: (event) => console.log('Column moved:', event)
});
// Imperative controls:
// board.addCard('todo', { id: 'task-4', title: 'Deploy to Cloudflare' });
// board.setFilter({ query: 'Tokens' });
// board.destroy();
```
---
## 2. Vue 3 Composition API Integration
Integrate effortlessly into Vue 3 templates using a template `ref`:
```vue
```
---
## 3. Svelte 5 Integration
Use standard Svelte `$effect` and action mounting:
```svelte
```
---