UIPackage
Menu

Framework

Change language

Boilerplate repo

Kanban

kanbanui

Composable compound Kanban primitive for building board, pipeline, and agile workflows with tactile card drag-and-drop mechanics.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/kanban.json
Named registry:npx shadcn-vue@latest add @uipkge/kanbanInstalls to:app/components/ui/kanban/

Variants

Loading interactive previews…

Examples in Use

Full Block

Multi-column agile board composing Kanban columns, drag-and-drop task cards, priority indicators, and member avatar stacks.

Explore full block
Loading interactive previews…

Props

NameType / ValuesDefaultRequired
isOver
'true''false'
optional
classHTMLAttributes['class']optional

Schema

Type aliases from this item's source — use them to shape the data you pass in.

KanbanMoveEvent
interface KanbanMoveEvent {
  cardId: string
  fromColumnId: string
  toColumnId: string
  toIndex?: number
}
KanbanContext
interface KanbanContext {
  draggingCardId: Ref<string | null>
  draggingColumnId: Ref<string | null>
  overColumnId: Ref<string | null>
  /** Set while a card is held by keyboard (Space), not by pointer drag. */
  grabbedCardId: Ref<string | null>
  setDraggingCard: (cardId: string | null, columnId: string | null) => void
  setOverColumn: (columnId: string | null) => void
  setGrabbedCard: (cardId: string | null) => void
  emitMove: (event: KanbanMoveEvent) => void
  /** Speak a message through the board's polite live region. */
  announce: (message: string) => void
}
KanbanColumnContext
interface KanbanColumnContext {
  columnId: string
  /** Column label, used to announce keyboard moves. Falls back to the id. */
  label: Ref<string | undefined>
}

Files installed (18)

  • app/components/ui/kanban/Kanban.vue1.7 kB
    <script setup lang="ts">
    import { provide, ref, type HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    import { KanbanContextKey, type KanbanMoveEvent } from './context'
    
    interface Props {
      class?: HTMLAttributes['class']
    }
    
    const props = defineProps<Props>()
    
    const emit = defineEmits<{
      'card-move': [event: KanbanMoveEvent]
    }>()
    
    const draggingCardId = ref<string | null>(null)
    const draggingColumnId = ref<string | null>(null)
    const overColumnId = ref<string | null>(null)
    const grabbedCardId = ref<string | null>(null)
    // Keyboard moves are silent to a screen reader — the card just appears
    // somewhere else. This region narrates pick up / move / drop / cancel.
    const announcement = ref('')
    
    function setDraggingCard(cardId: string | null, columnId: string | null) {
      draggingCardId.value = cardId
      draggingColumnId.value = columnId
    }
    
    function setOverColumn(columnId: string | null) {
      overColumnId.value = columnId
    }
    
    function setGrabbedCard(cardId: string | null) {
      grabbedCardId.value = cardId
    }
    
    function emitMove(event: KanbanMoveEvent) {
      emit('card-move', event)
    }
    
    function announce(message: string) {
      // Re-assigning the same string would not re-trigger the live region.
      announcement.value = announcement.value === message ? `${message} ` : message
    }
    
    provide(KanbanContextKey, {
      draggingCardId,
      draggingColumnId,
      overColumnId,
      grabbedCardId,
      setDraggingCard,
      setOverColumn,
      setGrabbedCard,
      emitMove,
      announce,
    })
    </script>
    
    <template>
      <div data-uipkge data-slot="kanban" :class="cn('w-full', props.class)">
        <slot />
        <div data-slot="kanban-live-region" class="sr-only" role="status" aria-live="polite" aria-atomic="true">
          {{ announcement }}
        </div>
      </div>
    </template>
    
  • app/components/ui/kanban/KanbanBoard.vue0.4 kB
  • app/components/ui/kanban/KanbanColumn.vue2 kB
  • app/components/ui/kanban/KanbanColumnHeader.vue0.4 kB
  • app/components/ui/kanban/KanbanColumnDot.vue0.4 kB
  • app/components/ui/kanban/KanbanColumnTitle.vue0.4 kB
  • app/components/ui/kanban/KanbanColumnCount.vue0.4 kB
  • app/components/ui/kanban/KanbanColumnAdd.vue0.7 kB
  • app/components/ui/kanban/KanbanColumnBody.vue0.4 kB
  • app/components/ui/kanban/KanbanColumnEmpty.vue0.5 kB
  • app/components/ui/kanban/KanbanCard.vue5.8 kB
  • app/components/ui/kanban/KanbanCardHeader.vue0.3 kB
  • app/components/ui/kanban/KanbanCardTitle.vue0.3 kB
  • app/components/ui/kanban/KanbanCardDescription.vue0.3 kB
  • app/components/ui/kanban/KanbanCardFooter.vue0.4 kB
  • app/components/ui/kanban/context.ts1.1 kB
  • app/components/ui/kanban/kanban.variants.ts1.4 kB
  • app/components/ui/kanban/index.ts1 kB

Raw manifest:https://uipkge.dev/r/vue/kanban.json