Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelog App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip
ARC UI ARC Radiant Components
v3.2 Docs Components Tokens Synthesizer
Getting StartedFrameworksServer Rendering Design TokensThemingTheme SynthesizerTypographyUtilities All ComponentsAccessibilityBrowser SupportChangelogContributingStats App ShellAspect GridAuth ShellCenterClusterContainerDashboard GridDockFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbar Anchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkSpeed DialStepper NavTabsTop BarTree View AccordionAspect RatioAvatarAvatar GroupCalloutCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSeparatorSkeletonSpinnerStackVideoVirtual List Activity HeatmapAnimated NumberBadgeChartClockComparisonCountdown TimerData GridData TableDescription ListDiffEvent CalendarGaugeJSON TreeKanbanKey ValueLevel MeterListMeterSparklineStatStepperTableTagTimelineUptimeValue CardWaveform BlockquoteCode BlockGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriter ButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputOTP InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree Select AlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuGuided TourHover CardInline MessageLoading OverlayModalNotification PanelPopoverProgressProgress ToastSheetSnackbarSpotlightToastTooltip

Kanban

A drag-and-drop kanban board driven by a `columns` data array. Cards can be dragged between and within columns with the pointer, or moved entirely from the keyboard with live screen-reader announcements. Column limits, tags, and descriptions are supported per card, and every move emits an `arc-card-move` event so the consumer can sync its source of truth.

Components Kanban
data interactive beta
<arc-kanban>

Overview

Kanban renders a horizontally scrolling row of columns from a single `columns` array — each column has an id, a title, an optional work-in-progress `limit`, and an `items` array of cards. Cards show a label, an optional two-line description, and an optional `arc-tag` chip. When a column has a limit, the header count renders as `count/limit` and switches to the error color when the column is over its limit. Dragging is pointer-based: press and move a card to lift it into a floating ghost that follows the cursor, with a horizontal indicator line showing exactly where the card will land. Dragging near the left or right edge of the board auto-scrolls it so long boards remain reachable. The component applies the move to its own internal copy immediately for instant feedback and emits `arc-card-move` with the card id, source column, target column, and final index — listen to that event to update your actual data store, then pass the new array back in. The keyboard model follows the accepted accessible kanban pattern: each column's card list is a single tab stop (roving tabindex), ArrowUp/ArrowDown move focus between cards, and ArrowLeft/ArrowRight jump between columns. Enter or Space grabs the focused card, arrows then move it within and across columns, Enter drops it (emitting the same `arc-card-move` event), and Escape cancels and returns the card to where it started. Every grab, move, drop, and cancel is announced through a polite live region.

Guidelines

When to use

  • Give every column and card a stable, unique id — moves and rendering are keyed on them
  • Listen to arc-card-move and update your source-of-truth data, then pass the new array back into columns
  • Set a limit on columns where work-in-progress caps matter — the badge flags overruns automatically
  • Keep card labels short and put detail in the description — it clamps to two lines
  • Use tag variants (primary, success, error, ...) to encode card category at a glance

When not to use

  • Do not mutate the columns array in place and expect a re-render — assign a new array instead
  • Do not rely on the component as the source of truth — its internal copy is for immediate feedback only
  • Do not put interactive controls (buttons, links) inside card labels — the whole card is the drag/keyboard target
  • Do not use kanban for a single static list — arc-sortable-list or arc-list is a better fit
  • Do not exceed a handful of columns without expecting horizontal scrolling — columns have a fixed 280px width

Features

  • Data-driven: one columns array renders the whole board — no manual markup per card
  • Pointer drag between and within columns with a floating drag ghost
  • Horizontal drop indicator line between cards shows the exact insertion point
  • Automatic horizontal board scrolling when dragging near the edges
  • Full keyboard move protocol: Enter/Space grabs, arrows move, Enter drops, Escape cancels
  • One tab stop per column (roving tabindex) — no tab-key marathons through every card
  • `aria-live` announcements for every grab, move, drop, and cancel
  • Optional per-column WIP limit with count/limit badge that turns error-colored when exceeded
  • Optional card description with a two-line clamp and an `arc-tag` chip per card
  • Empty columns render a subtle dashed drop zone that highlights during drag
  • `arc-card-move` and `arc-card-click` events for syncing external state
  • Styleable via ::part — board, column, column-header, card and more

Preview

Usage

This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.

<arc-kanban id="board"></arc-kanban>

<script type="module">
  import '@arclux/arc-ui/kanban';

  const board = document.querySelector('#board');
  let columns = [
    { id: 'todo', title: 'To Do', items: [
      { id: 't1', label: 'Design onboarding flow', description: 'Draft wireframes for the first-run experience.', tag: 'Design', variant: 'secondary' },
      { id: 't2', label: 'Audit color tokens' }
    ]},
    { id: 'doing', title: 'In Progress', limit: 2, items: [
      { id: 'd1', label: 'Refactor auth middleware', tag: 'Backend', variant: 'primary' }
    ]},
    { id: 'done', title: 'Done', items: [
      { id: 'x1', label: 'Set up CI pipeline', tag: 'Infra' }
    ]}
  ];
  board.columns = columns;

  // Sync the source of truth from move events
  board.addEventListener('arc-card-move', (e) => {
    const { cardId, fromColumn, toColumn, index } = e.detail;
    columns = moveCard(columns, cardId, fromColumn, toColumn, index);
  });
</script>
import { useState } from 'react';
import { Kanban } from '@arclux/arc-ui-react';

const initial = [
  { id: 'todo', title: 'To Do', items: [
    { id: 't1', label: 'Design onboarding flow', description: 'Draft wireframes for the first-run experience.', tag: 'Design', variant: 'secondary' },
    { id: 't2', label: 'Audit color tokens' }
  ]},
  { id: 'doing', title: 'In Progress', limit: 2, items: [
    { id: 'd1', label: 'Refactor auth middleware', tag: 'Backend', variant: 'primary' }
  ]},
  { id: 'done', title: 'Done', items: [
    { id: 'x1', label: 'Set up CI pipeline', tag: 'Infra' }
  ]}
];

export function Board() {
  const [columns, setColumns] = useState(initial);

  return (
    <Kanban
      columns={columns}
      onArcCardMove={(e) => {
        const { cardId, fromColumn, toColumn, index } = e.detail;
        setColumns((cols) => moveCard(cols, cardId, fromColumn, toColumn, index));
      }}
      onArcCardClick={(e) => openCardDetail(e.detail.cardId)}
    />
  );
}
<script setup>
import { ref } from 'vue';
import { Kanban } from '@arclux/arc-ui-vue';

const columns = ref([
  { id: 'todo', title: 'To Do', items: [
    { id: 't1', label: 'Design onboarding flow', tag: 'Design', variant: 'secondary' }
  ]},
  { id: 'doing', title: 'In Progress', limit: 2, items: [
    { id: 'd1', label: 'Refactor auth middleware', tag: 'Backend', variant: 'primary' }
  ]},
  { id: 'done', title: 'Done', items: [] }
]);

function onMove(e) {
  const { cardId, fromColumn, toColumn, index } = e.detail;
  columns.value = moveCard(columns.value, cardId, fromColumn, toColumn, index);
}
</script>

<template>
  <Kanban :columns="columns" @arc-card-move="onMove" />
</template>
<script>
  import { Kanban } from '@arclux/arc-ui-svelte';

  let columns = [
    { id: 'todo', title: 'To Do', items: [
      { id: 't1', label: 'Design onboarding flow', tag: 'Design', variant: 'secondary' }
    ]},
    { id: 'doing', title: 'In Progress', limit: 2, items: [
      { id: 'd1', label: 'Refactor auth middleware', tag: 'Backend', variant: 'primary' }
    ]},
    { id: 'done', title: 'Done', items: [] }
  ];

  function onMove(e) {
    const { cardId, fromColumn, toColumn, index } = e.detail;
    columns = moveCard(columns, cardId, fromColumn, toColumn, index);
  }
</script>

<Kanban {columns} on:arc-card-move={onMove} />
import { Component } from '@angular/core';
import { Kanban } from '@arclux/arc-ui-angular';

@Component({
  imports: [Kanban],
  template: `
    <arc-kanban [columns]="columns" (arcCardMove)="onMove($event)"></arc-kanban>
  `,
})
export class BoardComponent {
  columns = [
    { id: 'todo', title: 'To Do', items: [
      { id: 't1', label: 'Design onboarding flow', tag: 'Design', variant: 'secondary' }
    ]},
    { id: 'doing', title: 'In Progress', limit: 2, items: [
      { id: 'd1', label: 'Refactor auth middleware', tag: 'Backend', variant: 'primary' }
    ]},
    { id: 'done', title: 'Done', items: [] }
  ];

  onMove(e: CustomEvent) {
    const { cardId, fromColumn, toColumn, index } = e.detail;
    this.columns = moveCard(this.columns, cardId, fromColumn, toColumn, index);
  }
}

API

columns Array<{id:string,title?:string,limit?:number,items:Array<{id:string,label:string,description?:string,tag?:string,variant?:string}>}> []
The data array that drives the board. Each entry becomes a column with a header (title plus count badge) and a list of cards. `limit` renders the count as `count/limit` and turns it error-colored when exceeded. Each card needs a unique `id` and a `label`; `description` renders below the label with a two-line clamp, and `tag` renders an arc-tag chip styled by `variant`. Set via JavaScript — it is not an HTML attribute. The component works on an internal copy for immediate drag feedback; sync your source of truth from `arc-card-move` and assign a new array to re-render.
disabled boolean false
Disables all pointer and keyboard interaction and dims the board.

Events

arc-card-move
Fired when a card is dropped in a new position (pointer or keyboard). detail: { cardId, fromColumn, toColumn, index } where index is the final position in the target column.
arc-card-click
Fired when a card is clicked without being dragged. detail: { cardId, columnId }.

See Also