# 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
``` ---