{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "kanban",
  "title": "Kanban",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/kanban/Kanban.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n  import type { KanbanMoveEvent } from './context'\n\n  export interface KanbanProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    /** Fired when a card moves within or across columns (pointer drop or keyboard move). */\n    oncardmove?: (event: KanbanMoveEvent) => void\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n  import { setKanbanContext } from './context'\n\n  let { class: className, children, oncardmove, ref = $bindable(null), ...restProps }: KanbanProps = $props()\n\n  let draggingCardId = $state<string | null>(null)\n  let draggingColumnId = $state<string | null>(null)\n  let overColumnId = $state<string | null>(null)\n  let grabbedCardId = $state<string | null>(null)\n  // Keyboard moves are silent to a screen reader — the card just appears\n  // somewhere else. This region narrates pick up / move / drop / cancel.\n  let announcement = $state('')\n\n  setKanbanContext({\n    get draggingCardId() {\n      return draggingCardId\n    },\n    get draggingColumnId() {\n      return draggingColumnId\n    },\n    get overColumnId() {\n      return overColumnId\n    },\n    get grabbedCardId() {\n      return grabbedCardId\n    },\n    setDraggingCard(cardId, columnId) {\n      draggingCardId = cardId\n      draggingColumnId = columnId\n    },\n    setOverColumn(columnId) {\n      overColumnId = columnId\n    },\n    setGrabbedCard(cardId) {\n      grabbedCardId = cardId\n    },\n    emitMove(event) {\n      oncardmove?.(event)\n    },\n    announce(message) {\n      // Re-assigning the same string would not re-trigger the live region.\n      announcement = announcement === message ? `${message} ` : message\n    },\n  })\n</script>\n\n<div bind:this={ref} data-uipkge=\"\" data-slot=\"kanban\" class={cn('w-full', className)} {...restProps}>\n  {@render children?.()}\n  <div data-slot=\"kanban-live-region\" class=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n    {announcement}\n  </div>\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/Kanban.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanBoard.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanBoardProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanBoardProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-slot=\"kanban-board\"\n  class={cn('flex w-full items-start gap-4 overflow-x-auto pb-4', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanBoard.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumn.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n    id: string\n    /** Accessible name for the column, also used in keyboard move\n     *  announcements (\"moved to In progress\"). Falls back to the id. */\n    label?: string\n    children?: Snippet<[{ isOver: boolean }]>\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n  import { getKanbanContext, setKanbanColumnContext } from './context'\n  import { kanbanColumnVariants } from './kanban.variants'\n\n  let { class: className, id, label, children, ref = $bindable(null), ...restProps }: KanbanColumnProps = $props()\n\n  const kanban = getKanbanContext()\n\n  const isOver = $derived(kanban?.overColumnId === id)\n\n  setKanbanColumnContext({\n    // svelte-ignore state_referenced_locally: column ids are static for the board's lifetime.\n    columnId: id,\n    get isOver() {\n      return isOver\n    },\n    get label() {\n      return label\n    },\n  })\n\n  function handleDragOver(e: DragEvent) {\n    e.preventDefault()\n    if (e.dataTransfer) {\n      e.dataTransfer.dropEffect = 'move'\n    }\n    if (kanban && kanban.overColumnId !== id) {\n      kanban.setOverColumn(id)\n    }\n  }\n\n  function handleDragLeave(e: DragEvent & { currentTarget: HTMLElement }) {\n    const currentTarget = e.currentTarget\n    const relatedTarget = e.relatedTarget as HTMLElement | null\n    if (currentTarget?.contains(relatedTarget)) return\n    if (kanban && kanban.overColumnId === id) {\n      kanban.setOverColumn(null)\n    }\n  }\n\n  function handleDrop(e: DragEvent) {\n    e.preventDefault()\n    if (kanban && kanban.draggingCardId && kanban.draggingColumnId) {\n      kanban.emitMove({\n        cardId: kanban.draggingCardId,\n        fromColumnId: kanban.draggingColumnId,\n        toColumnId: id,\n      })\n    }\n    kanban?.setDraggingCard(null, null)\n    kanban?.setOverColumn(null)\n  }\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"kanban-column\"\n  role=\"group\"\n  aria-label={label ?? id}\n  data-column-id={id}\n  data-over={isOver ? '' : undefined}\n  class={cn(kanbanColumnVariants({ isOver }), className)}\n  ondragover={handleDragOver}\n  ondragleave={handleDragLeave}\n  ondrop={handleDrop}\n  {...restProps}\n>\n  {@render children?.({ isOver })}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumn.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnHeader.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnHeaderProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanColumnHeaderProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-slot=\"kanban-column-header\"\n  class={cn('flex items-center justify-between gap-2 px-1 py-0.5', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnHeader.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnDot.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnDotProps extends HTMLAttributes<HTMLSpanElement> {\n    color?: string\n    ref?: HTMLSpanElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, color = 'bg-primary', ref = $bindable(null), ...restProps }: KanbanColumnDotProps = $props()\n</script>\n\n<span bind:this={ref} data-slot=\"kanban-column-dot\" class={cn('size-2 shrink-0 rounded-full', color, className)} {...restProps}></span>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnDot.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnTitle.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnTitleProps extends HTMLAttributes<HTMLHeadingElement> {\n    children?: Snippet\n    ref?: HTMLHeadingElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanColumnTitleProps = $props()\n</script>\n\n<h3\n  bind:this={ref}\n  data-slot=\"kanban-column-title\"\n  class={cn('text-foreground text-sm font-semibold tracking-tight', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</h3>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnTitle.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnCount.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnCountProps extends HTMLAttributes<HTMLSpanElement> {\n    count?: number | string\n    children?: Snippet\n    ref?: HTMLSpanElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, count, children, ref = $bindable(null), ...restProps }: KanbanColumnCountProps = $props()\n</script>\n\n<span\n  bind:this={ref}\n  data-slot=\"kanban-column-count\"\n  class={cn('bg-muted text-muted-foreground rounded-md px-1.5 py-0.5 text-xs font-medium tabular-nums', className)}\n  {...restProps}\n>\n  {#if children}\n    {@render children()}\n  {:else}\n    {count}\n  {/if}\n</span>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnCount.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnAdd.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLButtonAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnAddProps extends HTMLButtonAttributes {\n    children?: Snippet\n    ref?: HTMLButtonElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n  import { Plus } from '@lucide/svelte'\n\n  let {\n    class: className,\n    type = 'button',\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: KanbanColumnAddProps = $props()\n</script>\n\n<button\n  bind:this={ref}\n  {type}\n  data-slot=\"kanban-column-add\"\n  class={cn(\n    'text-muted-foreground hover:bg-background hover:text-foreground focus-visible:ring-ring inline-flex size-6 items-center justify-center rounded-md transition-colors focus-visible:ring-1 focus-visible:outline-none',\n    className,\n  )}\n  {...restProps}\n>\n  {#if children}\n    {@render children()}\n  {:else}\n    <Plus class=\"size-3.5\" />\n  {/if}\n</button>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnAdd.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnBody.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnBodyProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanColumnBodyProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-slot=\"kanban-column-body\"\n  class={cn('flex flex-1 flex-col gap-2 overflow-y-auto py-1', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnBody.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanColumnEmpty.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanColumnEmptyProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanColumnEmptyProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-slot=\"kanban-column-empty\"\n  class={cn(\n    'border-border/60 text-muted-foreground/60 flex flex-1 items-center justify-center rounded-lg border border-dashed py-8 text-xs',\n    className,\n  )}\n  {...restProps}\n>\n  {#if children}\n    {@render children()}\n  {:else}\n    No cards\n  {/if}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanColumnEmpty.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanCard.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n    id: string\n    disabled?: boolean\n    /** Disable the keyboard grab (Space / arrows) while leaving pointer\n     *  dragging intact. Default: enabled. */\n    keyboardDraggable?: boolean\n    children?: Snippet<[{ isDragging: boolean; isGrabbed: boolean }]>\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n  import { getKanbanColumnContext, getKanbanContext } from './context'\n  import { kanbanCardVariants } from './kanban.variants'\n\n  let {\n    class: className,\n    id,\n    disabled = false,\n    keyboardDraggable = true,\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: KanbanCardProps = $props()\n\n  const kanban = getKanbanContext()\n  const column = getKanbanColumnContext()\n\n  const isGrabbed = $derived(kanban?.grabbedCardId === id)\n  const isDragging = $derived(kanban?.draggingCardId === id || isGrabbed)\n\n  const canKeyboardDrag = $derived(keyboardDraggable && !disabled && !!kanban && !!column)\n\n  function columnName(el: HTMLElement | null) {\n    return el?.getAttribute('aria-label') || el?.dataset.columnId || 'column'\n  }\n\n  /** Ordered, enabled columns of the board this card sits in. */\n  function boardColumns(): HTMLElement[] {\n    const board = ref?.closest('[data-slot=\"kanban\"]') ?? document\n    return Array.from(board.querySelectorAll<HTMLElement>('[data-slot=\"kanban-column\"]'))\n  }\n\n  function focusSelfAfterMove() {\n    // The consumer owns the data, so the card is re-rendered (often as a new\n    // node) in its new column. Re-find it by id and restore focus.\n    requestAnimationFrame(() => {\n      const moved = document.querySelector<HTMLElement>(`[data-slot=\"kanban-card\"][data-card-id=\"${id}\"]`)\n      moved?.focus()\n    })\n  }\n\n  function grab() {\n    if (!canKeyboardDrag) return\n    kanban!.setGrabbedCard(id)\n    kanban!.setDraggingCard(id, column!.columnId)\n    kanban!.setOverColumn(column!.columnId)\n    kanban!.announce(`Picked up card. Use the arrow keys to move it, space to drop, escape to cancel.`)\n  }\n\n  function release(cancelled: boolean) {\n    if (!kanban) return\n    kanban.setGrabbedCard(null)\n    kanban.setDraggingCard(null, null)\n    kanban.setOverColumn(null)\n    kanban.announce(cancelled ? 'Move cancelled.' : 'Card dropped.')\n  }\n\n  function moveToColumn(delta: -1 | 1) {\n    const columns = boardColumns()\n    const currentIdx = columns.findIndex((el) => el.dataset.columnId === column!.columnId)\n    if (currentIdx === -1) return\n    const target = columns[currentIdx + delta]\n    // Deliberately not wrapping: running off the end of a board should stop,\n    // not teleport the card back to the first column.\n    if (!target?.dataset.columnId) return\n    kanban!.emitMove({\n      cardId: id,\n      fromColumnId: column!.columnId,\n      toColumnId: target.dataset.columnId,\n    })\n    kanban!.setDraggingCard(id, target.dataset.columnId)\n    kanban!.setOverColumn(target.dataset.columnId)\n    kanban!.announce(`Moved to ${columnName(target)}.`)\n    focusSelfAfterMove()\n  }\n\n  function moveWithinColumn(delta: -1 | 1) {\n    const columnEl = ref?.closest<HTMLElement>('[data-slot=\"kanban-column\"]')\n    if (!columnEl) return\n    const cards = Array.from(columnEl.querySelectorAll<HTMLElement>('[data-slot=\"kanban-card\"]'))\n    const currentIdx = cards.findIndex((el) => el.dataset.cardId === id)\n    if (currentIdx === -1) return\n    const targetIdx = currentIdx + delta\n    if (targetIdx < 0 || targetIdx > cards.length - 1) return\n    kanban!.emitMove({\n      cardId: id,\n      fromColumnId: column!.columnId,\n      toColumnId: column!.columnId,\n      toIndex: targetIdx,\n    })\n    kanban!.announce(`Position ${targetIdx + 1} of ${cards.length}.`)\n    focusSelfAfterMove()\n  }\n\n  function handleKeydown(e: KeyboardEvent) {\n    if (!canKeyboardDrag) return\n\n    if (e.key === ' ' || e.key === 'Spacebar') {\n      e.preventDefault()\n      if (isGrabbed) release(false)\n      else grab()\n      return\n    }\n    if (!isGrabbed) return\n    if (e.key === 'Escape') {\n      e.preventDefault()\n      release(true)\n      return\n    }\n    if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n      e.preventDefault()\n      moveToColumn(e.key === 'ArrowLeft' ? -1 : 1)\n      return\n    }\n    if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n      e.preventDefault()\n      moveWithinColumn(e.key === 'ArrowUp' ? -1 : 1)\n    }\n  }\n\n  function handleBlur() {\n    // A grabbed card that loses focus (click elsewhere, Tab) would otherwise\n    // stay stuck in the held state with no way back to it.\n    if (isGrabbed) release(true)\n  }\n\n  function handleDragStart(e: DragEvent) {\n    if (disabled) {\n      e.preventDefault()\n      return\n    }\n    if (e.dataTransfer) {\n      e.dataTransfer.effectAllowed = 'move'\n      e.dataTransfer.setData('text/plain', id)\n    }\n    kanban?.setDraggingCard(id, column?.columnId ?? null)\n  }\n\n  function handleDragEnd() {\n    kanban?.setDraggingCard(null, null)\n    kanban?.setOverColumn(null)\n  }\n</script>\n\n<!-- `role=\"button\"` rather than a real <button> so consumers can nest\n     interactive content (menus, links) inside a card — the HTML spec\n     forbids that inside <button>. Matches <BoardCard>. -->\n<!-- svelte-ignore a11y_no_noninteractive_tabindex: role is button whenever the card is focusable -->\n<div\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"kanban-card\"\n  data-card-id={id}\n  data-state={isGrabbed ? 'grabbed' : isDragging ? 'dragging' : 'idle'}\n  data-disabled={disabled || undefined}\n  role={canKeyboardDrag ? 'button' : undefined}\n  tabindex={canKeyboardDrag ? 0 : undefined}\n  aria-disabled={disabled || undefined}\n  aria-roledescription={canKeyboardDrag ? 'draggable card' : undefined}\n  aria-pressed={canKeyboardDrag ? isGrabbed : undefined}\n  draggable={!disabled}\n  class={cn(kanbanCardVariants({ isDragging }), disabled && 'pointer-events-none opacity-50', className)}\n  ondragstart={handleDragStart}\n  ondragend={handleDragEnd}\n  onkeydown={handleKeydown}\n  onblur={handleBlur}\n  {...restProps}\n>\n  {@render children?.({ isDragging, isGrabbed })}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanCard.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanCardHeader.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanCardHeaderProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanCardHeaderProps = $props()\n</script>\n\n<div bind:this={ref} data-slot=\"kanban-card-header\" class={cn('flex flex-col gap-1', className)} {...restProps}>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanCardHeader.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanCardTitle.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanCardTitleProps extends HTMLAttributes<HTMLParagraphElement> {\n    children?: Snippet\n    ref?: HTMLParagraphElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanCardTitleProps = $props()\n</script>\n\n<p\n  bind:this={ref}\n  data-slot=\"kanban-card-title\"\n  class={cn('text-foreground text-sm leading-snug font-medium', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</p>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanCardTitle.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanCardDescription.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanCardDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {\n    children?: Snippet\n    ref?: HTMLParagraphElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanCardDescriptionProps = $props()\n</script>\n\n<p\n  bind:this={ref}\n  data-slot=\"kanban-card-description\"\n  class={cn('text-muted-foreground line-clamp-2 text-xs', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</p>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanCardDescription.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/KanbanCardFooter.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface KanbanCardFooterProps extends HTMLAttributes<HTMLDivElement> {\n    children?: Snippet\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: KanbanCardFooterProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-slot=\"kanban-card-footer\"\n  class={cn('text-muted-foreground mt-1 flex items-center justify-between gap-2 pt-1 text-xs', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/KanbanCardFooter.svelte"
    },
    {
      "path": "packages/registry-svelte/components/kanban/context.ts",
      "content": "import { getContext, setContext } from 'svelte'\n\nexport interface KanbanMoveEvent {\n  cardId: string\n  fromColumnId: string\n  toColumnId: string\n  toIndex?: number\n}\n\nexport interface KanbanContext {\n  readonly draggingCardId: string | null\n  readonly draggingColumnId: string | null\n  readonly overColumnId: string | null\n  /** Set while a card is held by keyboard (Space), not by pointer drag. */\n  readonly grabbedCardId: string | null\n  setDraggingCard: (cardId: string | null, columnId: string | null) => void\n  setOverColumn: (columnId: string | null) => void\n  setGrabbedCard: (cardId: string | null) => void\n  emitMove: (event: KanbanMoveEvent) => void\n  /** Speak a message through the board's polite live region. */\n  announce: (message: string) => void\n}\n\nconst KanbanContextKey = Symbol('KanbanContext')\n\nexport function setKanbanContext(ctx: KanbanContext): void {\n  setContext(KanbanContextKey, ctx)\n}\n\nexport function getKanbanContext(): KanbanContext | undefined {\n  return getContext<KanbanContext | undefined>(KanbanContextKey)\n}\n\nexport interface KanbanColumnContext {\n  columnId: string\n  /** True while a dragged (pointer or keyboard-grabbed) card hovers this column. React twin: `isOver`. */\n  readonly isOver: boolean\n  /** Column label, used to announce keyboard moves. Falls back to the id. */\n  readonly label: string | undefined\n}\n\nconst KanbanColumnContextKey = Symbol('KanbanColumnContext')\n\nexport function setKanbanColumnContext(ctx: KanbanColumnContext): void {\n  setContext(KanbanColumnContextKey, ctx)\n}\n\nexport function getKanbanColumnContext(): KanbanColumnContext | undefined {\n  return getContext<KanbanColumnContext | undefined>(KanbanColumnContextKey)\n}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/context.ts"
    },
    {
      "path": "packages/registry-svelte/components/kanban/kanban.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const kanbanColumnVariants = cva(\n  'flex min-h-[300px] w-72 shrink-0 flex-col gap-2 rounded-xl border bg-muted/40 p-3 transition-colors duration-200 ease-out',\n  {\n    variants: {\n      isOver: {\n        true: 'border-primary/50 bg-primary/5 ring-2 ring-primary/20',\n        false: 'border-border/70',\n      },\n    },\n    defaultVariants: {\n      isOver: false,\n    },\n  },\n)\n\nexport const kanbanCardVariants = cva(\n  'group relative flex cursor-grab flex-col gap-2 rounded-lg border bg-card p-3 text-card-foreground shadow-xs transition-[border-color,box-shadow,opacity] duration-150 ease-out active:cursor-grabbing hover:border-border hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n  {\n    variants: {\n      isDragging: {\n        // Keyboard grab reuses this state, so the held card stays fully\n        // opaque — unlike a pointer drag there is no drag image to look at.\n        true: 'shadow-none ring-2 ring-primary/40 opacity-100 data-[state=dragging]:opacity-40',\n        false: 'opacity-100',\n      },\n    },\n    defaultVariants: {\n      isDragging: false,\n    },\n  },\n)\n\nexport type KanbanColumnVariantsProps = VariantProps<typeof kanbanColumnVariants>\nexport type KanbanCardVariantsProps = VariantProps<typeof kanbanCardVariants>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/kanban.variants.ts"
    },
    {
      "path": "packages/registry-svelte/components/kanban/index.ts",
      "content": "export { default as Kanban, type KanbanProps } from './Kanban.svelte'\nexport { default as KanbanBoard, type KanbanBoardProps } from './KanbanBoard.svelte'\nexport { default as KanbanColumn, type KanbanColumnProps } from './KanbanColumn.svelte'\nexport { default as KanbanColumnHeader, type KanbanColumnHeaderProps } from './KanbanColumnHeader.svelte'\nexport { default as KanbanColumnDot, type KanbanColumnDotProps } from './KanbanColumnDot.svelte'\nexport { default as KanbanColumnTitle, type KanbanColumnTitleProps } from './KanbanColumnTitle.svelte'\nexport { default as KanbanColumnCount, type KanbanColumnCountProps } from './KanbanColumnCount.svelte'\nexport { default as KanbanColumnAdd, type KanbanColumnAddProps } from './KanbanColumnAdd.svelte'\nexport { default as KanbanColumnBody, type KanbanColumnBodyProps } from './KanbanColumnBody.svelte'\nexport { default as KanbanColumnEmpty, type KanbanColumnEmptyProps } from './KanbanColumnEmpty.svelte'\nexport { default as KanbanCard, type KanbanCardProps } from './KanbanCard.svelte'\nexport { default as KanbanCardHeader, type KanbanCardHeaderProps } from './KanbanCardHeader.svelte'\nexport { default as KanbanCardTitle, type KanbanCardTitleProps } from './KanbanCardTitle.svelte'\nexport { default as KanbanCardDescription, type KanbanCardDescriptionProps } from './KanbanCardDescription.svelte'\nexport { default as KanbanCardFooter, type KanbanCardFooterProps } from './KanbanCardFooter.svelte'\nexport * from './kanban.variants'\nexport * from './context'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/kanban/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "@lucide/svelte"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Composable compound Kanban primitive for building board, pipeline, and agile workflows with tactile card drag-and-drop mechanics.",
  "categories": [
    "data-display",
    "layout"
  ]
}