{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "board",
  "title": "Board — compositional kanban / sortable-list primitive",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/board/Board.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n  import type { BoardAcceptsFn, BoardDensity, BoardOrientation, BoardState } from './context'\n\n  export interface BoardProps extends HTMLAttributes<HTMLDivElement> {\n    /**\n     * External state snapshot (`draggingId` / `draggingIds` / `dragOverLaneId` /\n     * `justMovedId` / `selectedIds`) — the Svelte equivalent of spreading\n     * React `useBoard`'s `state` into `<Board>`. Read-only: the board never\n     * writes back (mutations flow through `moveItem` / `toggleSelection`).\n     * When both are given, `state` fields win over the individual props below.\n     */\n    state?: BoardState\n    orientation?: BoardOrientation\n    density?: BoardDensity\n    /** Transition name applied by BoardLaneBody. Defaults to `motion-list`. */\n    motion?: string\n    /** Predicate run per drop. Defaults to always-accept. */\n    accepts?: BoardAcceptsFn\n    /** Imperative move from a parent's board state helper. */\n    moveItem?: (itemId: string | string[], toLaneId: string, toIndex?: number) => void\n    /** External state — pass `state.draggingId` etc. from the board state helper. */\n    draggingId?: string | null\n    draggingIds?: readonly string[]\n    dragOverLaneId?: string | null\n    justMovedId?: string | null\n    selectedIds?: ReadonlySet<string>\n    /** Optional toggle/clearSelection from the board state helper so the\n     *  primitive can expose selection mutations through context (consumed\n     *  by the BoardCard click handler). Both default to no-ops, so Board\n     *  still works with consumers that don't wire selection. */\n    toggleSelection?: (itemId: string, additive?: boolean) => void\n    clearSelection?: () => void\n    registerAllowedLanes?: (cardId: string, lanes: readonly string[] | undefined) => void\n    unregisterAllowedLanes?: (cardId: string) => void\n    registerLaneDisabled?: (laneId: string, disabled: boolean) => void\n    unregisterLaneDisabled?: (laneId: string) => void\n    isLaneAcceptingFor?: (laneId: string) => boolean\n    /** The root <div>, via `bind:ref`. */\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { setContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { BOARD_CONTEXT, type BoardContext } from './context'\n\n  // NOTE: the public prop is `state`, but it is destructured as `boardState`\n  // — a local named `state` would shadow the `$state` rune used below.\n  let {\n    class: className,\n    state: boardState,\n    orientation = 'horizontal',\n    density = 'default',\n    motion = 'motion-list',\n    accepts = () => true,\n    moveItem = () => {},\n    draggingId = null,\n    draggingIds = [],\n    dragOverLaneId = null,\n    justMovedId = null,\n    selectedIds = new Set<string>(),\n    toggleSelection = () => {},\n    clearSelection = () => {},\n    registerAllowedLanes = () => {},\n    unregisterAllowedLanes = () => {},\n    registerLaneDisabled = () => {},\n    unregisterLaneDisabled = () => {},\n    isLaneAcceptingFor = () => true,\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: BoardProps = $props()\n\n  let laneIds = $state<symbol[]>([])\n\n  // Getters keep the context reactive: children reading these inside\n  // `$derived` re-run when the parent's props change.\n  setContext<BoardContext>(BOARD_CONTEXT, {\n    get orientation() {\n      return orientation\n    },\n    get density() {\n      return density\n    },\n    get motion() {\n      return motion\n    },\n    get draggingId() {\n      return boardState?.draggingId ?? draggingId\n    },\n    get draggingIds() {\n      return boardState?.draggingIds ?? draggingIds\n    },\n    get dragOverLaneId() {\n      return boardState?.dragOverLaneId ?? dragOverLaneId\n    },\n    get justMovedId() {\n      return boardState?.justMovedId ?? justMovedId\n    },\n    get selectedIds() {\n      return boardState?.selectedIds ?? selectedIds\n    },\n    get accepts() {\n      return accepts\n    },\n    moveItem: (...args) => moveItem(...args),\n    toggleSelection: (...args) => toggleSelection(...args),\n    clearSelection: () => clearSelection(),\n    registerAllowedLanes: (...args) => registerAllowedLanes(...args),\n    unregisterAllowedLanes: (...args) => unregisterAllowedLanes(...args),\n    registerLaneDisabled: (...args) => registerLaneDisabled(...args),\n    unregisterLaneDisabled: (...args) => unregisterLaneDisabled(...args),\n    isLaneAcceptingFor: (laneId) => isLaneAcceptingFor(laneId),\n    get laneIds() {\n      return laneIds\n    },\n    registerLane: (id) => {\n      if (!laneIds.includes(id)) laneIds.push(id)\n    },\n    unregisterLane: (id) => {\n      laneIds = laneIds.filter((i) => i !== id)\n    },\n  })\n</script>\n\n<div bind:this={ref} data-uipkge=\"\" data-slot=\"board\" data-orientation={orientation} class={cn('w-full', className)} {...restProps}>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/Board.svelte"
    },
    {
      "path": "packages/registry-svelte/components/board/BoardLane.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n  import type { BoardLaneVariantsProps } from './board.variants'\n\n  export interface BoardLaneSlotProps {\n    isDragOver: boolean\n    isAccepting: boolean\n    disabled: boolean\n  }\n\n  export interface BoardLaneProps\n    extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'ondragover' | 'ondrop' | 'ondragleave'> {\n    id: string\n    tone?: BoardLaneVariantsProps['tone']\n    /** Disable drops on this lane. Cards inside still render and stay\n     *  draggable; only the drop target is inert + visually dimmed. */\n    disabled?: boolean\n    children?: Snippet<[BoardLaneSlotProps]>\n    /** The lane <div>, via `bind:ref`. */\n    ref?: HTMLDivElement | null\n    ondragover?: (e: DragEvent) => void\n    ondrop?: (e: DragEvent) => void\n    ondragleave?: () => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext, onMount, setContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { BOARD_CONTEXT, BOARD_LANE_CONTEXT, type BoardContext, type BoardLaneContext } from './context'\n  import { boardLaneVariants } from './board.variants'\n\n  let {\n    class: className,\n    id,\n    tone = 'default',\n    disabled = false,\n    children,\n    ref = $bindable(null),\n    ondragover,\n    ondrop,\n    ondragleave,\n    ...restProps\n  }: BoardLaneProps = $props()\n\n  const boardContext = getContext<BoardContext | null>(BOARD_CONTEXT)\n  if (!boardContext) {\n    throw new Error('<BoardLane> must be a descendant of <Board>.')\n  }\n  // Non-nullable alias: the throw above proves presence, and a plain const\n  // keeps the narrowing inside every closure below.\n  const board: BoardContext = boardContext\n\n  const laneSymbol = Symbol('BoardLane')\n  onMount(() => {\n    board.registerLane(laneSymbol)\n    return () => board.unregisterLane(laneSymbol)\n  })\n\n  // Keep the disabled registry in sync when the id/disabled props change.\n  let prevId = id\n  $effect(() => {\n    if (prevId !== id) {\n      board.unregisterLaneDisabled(prevId)\n      prevId = id\n    }\n    board.registerLaneDisabled(id, disabled)\n  })\n  onMount(() => {\n    return () => board.unregisterLaneDisabled(prevId)\n  })\n\n  const isDragOver = $derived(board.dragOverLaneId === id)\n  const isAccepting = $derived.by(() => {\n    if (!board.draggingId) return false\n    if (disabled) return false\n    return board.isLaneAcceptingFor(id)\n  })\n\n  setContext<BoardLaneContext>(BOARD_LANE_CONTEXT, {\n    get laneId() {\n      return id\n    },\n    get isDragOver() {\n      return isDragOver\n    },\n    get isAccepting() {\n      return isAccepting\n    },\n    get disabled() {\n      return disabled\n    },\n  })\n\n  const state = $derived.by<'idle' | 'over' | 'rejecting'>(() => {\n    if (!isDragOver) return 'idle'\n    return isAccepting ? 'over' : 'rejecting'\n  })\n\n  function onDragOver(e: DragEvent) {\n    // Call unconditionally so the state helper can set dragOverLaneId\n    // (drives the rejecting-ring visual). The helper's isAllowed\n    // checks the disabled-lane registry and refuses preventDefault when\n    // it should — the browser's own refuse-to-drop semantics + our\n    // rejecting visual cover the rest.\n    ondragover?.(e)\n  }\n  function onDrop(e: DragEvent) {\n    if (disabled) {\n      e.preventDefault()\n      return\n    }\n    ondrop?.(e)\n  }\n  function onDragLeave() {\n    ondragleave?.()\n  }\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"board-lane\"\n  data-board-lane=\"\"\n  data-lane-id={id}\n  data-state={state}\n  data-disabled={disabled || undefined}\n  aria-disabled={disabled || undefined}\n  class={cn(\n    boardLaneVariants({ tone, state }),\n    // Disabled lane dims only the lane chrome (border, background,\n    // header). Cards inside stay legible — the rejection signal is\n    // carried by the no-drop cursor + the missing accept-ring.\n    disabled && 'opacity-80 [&>[data-slot=board-lane-header]]:opacity-60',\n    className,\n  )}\n  ondragover={onDragOver}\n  ondrop={onDrop}\n  ondragleave={onDragLeave}\n  {...restProps}\n>\n  {@render children?.({ isDragOver, isAccepting, disabled })}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/BoardLane.svelte"
    },
    {
      "path": "packages/registry-svelte/components/board/BoardLaneHeader.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface BoardLaneHeaderProps extends HTMLAttributes<HTMLDivElement> {\n    /** The header <div>, via `bind:ref`. */\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 }: BoardLaneHeaderProps = $props()\n</script>\n\n<div bind:this={ref} data-uipkge=\"\" data-slot=\"board-lane-header\" class={cn('flex items-center justify-between gap-2', className)} {...restProps}>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/BoardLaneHeader.svelte"
    },
    {
      "path": "packages/registry-svelte/components/board/BoardLaneBody.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface BoardLaneBodyProps extends HTMLAttributes<HTMLDivElement> {\n    /** Override the transition name. Defaults to the board-level motion preset. */\n    motion?: string\n    /** The body <div>, via `bind:ref`. */\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { BOARD_CONTEXT, type BoardContext } from './context'\n\n  let { class: className, motion = undefined, children, ref = $bindable(null), ...restProps }: BoardLaneBodyProps =\n    $props()\n\n  const board = getContext<BoardContext | null>(BOARD_CONTEXT)\n  const motionName = $derived(motion ?? board?.motion ?? 'motion-list')\n</script>\n\n<!-- Inner padding (py-1 / px-0.5) reserves breathing room for the\n     per-card hover-lift (-translate-y-0.5), the focus / drag / moved\n     rings (ring-2 + ring-offset-1 ≈ 3px outward), and the hover\n     shadow halo. Without it, the first / last cards' hover state\n     crops against the overflow-y-auto edge. pr-1 still wins on the\n     right so the thin scrollbar has a gutter.\n     NOTE: the Vue twin wraps the slot in a TransitionGroup for FLIP\n     reorder animation. Slotted children can't carry Svelte's\n     `animate:flip`, so reorder motion comes from the card-level CSS\n     transitions only; `motion` is kept for API parity and exposed as\n     a data attribute for consumer CSS hooks. -->\n<div\n  bind:this={ref}\n  data-uipkge=\"\"\n  data-slot=\"board-lane-body\"\n  data-motion={motionName}\n  class={cn('flex min-h-0 flex-1 [scrollbar-width:thin] flex-col gap-2 overflow-y-auto px-0.5 py-1 pr-1', className)}\n  {...restProps}\n>\n  <div class=\"relative flex flex-col gap-2\">\n    {@render children?.()}\n  </div>\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/BoardLaneBody.svelte"
    },
    {
      "path": "packages/registry-svelte/components/board/BoardLaneEmpty.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface BoardLaneEmptyProps extends HTMLAttributes<HTMLDivElement> {\n    /** Show only when this is true (consumer wires from `lane.length === 0`). */\n    when?: boolean\n    /** The empty-state <div>, via `bind:ref`. */\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, when = undefined, children, ref = $bindable(null), ...restProps }: BoardLaneEmptyProps =\n    $props()\n</script>\n\n{#if when !== false}\n  <div\n    bind:this={ref}\n    data-uipkge=\"\"\n    data-slot=\"board-lane-empty\"\n    class={cn(\n      'text-muted-foreground/70 border-border/60 rounded-lg border border-dashed py-6 text-center text-xs',\n      className,\n    )}\n    {...restProps}\n  >\n    {@render children?.()}\n  </div>\n{/if}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/BoardLaneEmpty.svelte"
    },
    {
      "path": "packages/registry-svelte/components/board/BoardCard.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface BoardCardProps\n    extends Omit<HTMLAttributes<HTMLDivElement>, 'onclick' | 'onkeydown' | 'ondragstart' | 'ondragend'> {\n    id: string\n    /** Disable keyboard grab (e.g. for read-only boards). Default: enabled. */\n    keyboardDraggable?: boolean\n    /** Disable the card entirely — non-draggable, non-clickable, dimmed.\n     *  Use for cards locked by a workflow rule or a server policy. */\n    disabled?: boolean\n    /** Per-card allow-list. When set, the card can only be dropped into\n     *  these lane ids; the state helper rejects any other target silently.\n     *  Omit to allow every lane the global `accepts` predicate permits. */\n    allowedLanes?: readonly string[]\n    /** Show the click-to-select chrome (ring + cmd/shift-click multi-select).\n     *  Default true. Turn off for read-only or single-tap-to-open boards. */\n    selectable?: boolean\n    /** The card <div>, via `bind:ref`. */\n    ref?: HTMLDivElement | null\n    /** Merged with the internal handlers (selection, Enter-to-activate). */\n    onclick?: (e: MouseEvent) => void\n    onkeydown?: (e: KeyboardEvent) => void\n    ondragstart?: (e: DragEvent) => void\n    ondragend?: () => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext, onMount, setContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { BOARD_CARD_CONTEXT, BOARD_CONTEXT, BOARD_LANE_CONTEXT } from './context'\n  import type { BoardCardContext, BoardContext, BoardLaneContext } from './context'\n  import { boardCardVariants } from './board.variants'\n\n  let {\n    class: className,\n    id,\n    keyboardDraggable = true,\n    disabled = false,\n    allowedLanes = undefined,\n    selectable = true,\n    children,\n    ref = $bindable(null),\n    onclick,\n    onkeydown,\n    ondragstart,\n    ondragend,\n    ...restProps\n  }: BoardCardProps = $props()\n\n  const boardContext = getContext<BoardContext | null>(BOARD_CONTEXT)\n  const laneContext = getContext<BoardLaneContext | null>(BOARD_LANE_CONTEXT)\n  if (!boardContext || !laneContext) {\n    throw new Error('<BoardCard> must be a descendant of <Board> and <BoardLane>.')\n  }\n  // Non-nullable aliases: the throw above proves presence, and plain consts\n  // keep the narrowing inside every closure below.\n  const board: BoardContext = boardContext\n  const lane: BoardLaneContext = laneContext\n\n  let isKeyboardGrabbed = $state(false)\n  // Either source of truth wins — `draggingIds` is the multi-select-aware\n  // list, `draggingId` is the single-anchor (backward-compat for consumers\n  // that don't pass the multi state through). Keyboard-grabbed adds the\n  // same visual without touching parent state.\n  const isDragging = $derived(\n    board.draggingIds.includes(id) || board.draggingId === id || isKeyboardGrabbed,\n  )\n  const isJustMoved = $derived(board.justMovedId === id)\n  const isSelected = $derived(board.selectedIds.has(id))\n\n  setContext<BoardCardContext>(BOARD_CARD_CONTEXT, {\n    get cardId() {\n      return id\n    },\n    get laneId() {\n      return lane.laneId\n    },\n    get isDragging() {\n      return isDragging\n    },\n    get isJustMoved() {\n      return isJustMoved\n    },\n    get isSelected() {\n      return isSelected\n    },\n    get disabled() {\n      return disabled\n    },\n  })\n\n  // Per-card allowed-lanes registry. Re-run if the id OR the list changes.\n  onMount(() => {\n    board.registerAllowedLanes(id, allowedLanes)\n    return () => board.unregisterAllowedLanes(prevCardId)\n  })\n  let prevCardId = id\n  $effect(() => {\n    if (prevCardId !== id) {\n      board.unregisterAllowedLanes(prevCardId)\n      prevCardId = id\n    }\n    board.registerAllowedLanes(id, allowedLanes)\n  })\n\n  const cardState = $derived.by<'idle' | 'dragging' | 'moved'>(() => {\n    if (isDragging) return 'dragging'\n    if (isJustMoved) return 'moved'\n    return 'idle'\n  })\n\n  function onKeydown(e: KeyboardEvent) {\n    if (!disabled) {\n      // Enter activates the card (default action) — calls onclick for the\n      // consumer to open a detail panel / navigate / etc. Native <button>\n      // gets this for free; we re-emit because the card root is a\n      // <div role=\"button\"> (chosen so consumers can nest <button>/links\n      // inside the card without violating \"no interactive content in a\n      // button\" — see commit context). The event still surfaces through\n      // the standard onclick handler.\n      if (e.key === 'Enter') {\n        e.preventDefault()\n        onclick?.(new MouseEvent('click', { bubbles: true, cancelable: true }))\n      } else if (keyboardDraggable) {\n        if (e.key === ' ') {\n          e.preventDefault()\n          isKeyboardGrabbed = !isKeyboardGrabbed\n        } else if (e.key === 'Escape' && isKeyboardGrabbed) {\n          e.preventDefault()\n          isKeyboardGrabbed = false\n        } else if (isKeyboardGrabbed && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n          e.preventDefault()\n          const allLanes = Array.from(document.querySelectorAll<HTMLElement>('[data-board-lane]')).filter(\n            (el) => !el.hasAttribute('data-disabled'),\n          )\n          const currentIdx = allLanes.findIndex((el) => el.dataset.laneId === lane.laneId)\n          if (currentIdx !== -1 && allLanes.length > 0) {\n            const delta = e.key === 'ArrowLeft' ? -1 : 1\n            const nextLane = allLanes[(currentIdx + delta + allLanes.length) % allLanes.length]\n            if (nextLane?.dataset.laneId) {\n              board.moveItem(id, nextLane.dataset.laneId)\n              requestAnimationFrame(() => {\n                const moved = document.querySelector<HTMLElement>(`[data-board-card-id=\"${id}\"]`)\n                moved?.focus()\n              })\n            }\n          }\n        } else if (isKeyboardGrabbed && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {\n          e.preventDefault()\n          const laneEl = ref?.closest('[data-board-lane]')\n          if (laneEl) {\n            const cards = Array.from(laneEl.querySelectorAll<HTMLElement>('[data-board-card]'))\n            const currentIdx = cards.findIndex((el) => el.dataset.boardCardId === id)\n            if (currentIdx !== -1) {\n              const delta = e.key === 'ArrowUp' ? -1 : 1\n              const targetIdx = Math.max(0, Math.min(cards.length - 1, currentIdx + delta))\n              if (targetIdx !== currentIdx) board.moveItem(id, lane.laneId, targetIdx)\n            }\n          }\n        }\n      }\n    }\n    onkeydown?.(e)\n  }\n\n  function onDragStart(e: DragEvent) {\n    if (disabled) {\n      e.preventDefault()\n      return\n    }\n    ondragstart?.(e)\n  }\n  function onDragEnd() {\n    ondragend?.()\n  }\n  function onClick(e: MouseEvent) {\n    if (disabled) {\n      e.preventDefault()\n      return\n    }\n    // Cmd/Ctrl/Shift+click toggles the selection (multi-select for drag);\n    // plain click clears any selection and just calls through to the\n    // consumer (typically opens a detail Sheet).\n    if (selectable && (e.metaKey || e.ctrlKey || e.shiftKey)) {\n      e.preventDefault()\n      board.toggleSelection(id, true)\n      return\n    }\n    if (board.draggingIds.includes(id)) return\n    if (board.selectedIds.size > 0) board.clearSelection()\n    onclick?.(e)\n  }\n</script>\n\n<!-- Root is `<div role=\"button\">` rather than `<button type=\"button\">`\n     so consumers can nest interactive content (buttons, links, menus)\n     inside cards. The HTML5 spec forbids interactive content inside a\n     <button>; browsers silently de-nest the inner element which\n     breaks its events. Enter is wired manually in onKeydown to match\n     the native button default-action behaviour. -->\n<div\n  bind:this={ref}\n  role=\"button\"\n  data-uipkge=\"\"\n  data-slot=\"board-card\"\n  data-board-card=\"\"\n  data-board-card-id={id}\n  data-state={cardState}\n  data-selected={isSelected || undefined}\n  data-disabled={disabled || undefined}\n  aria-disabled={disabled || undefined}\n  aria-pressed={selectable ? isSelected : undefined}\n  class={cn(\n    boardCardVariants({ state: cardState }),\n    isSelected && 'ring-primary/60 ring-offset-background ring-2 ring-offset-1',\n    disabled && 'pointer-events-none cursor-not-allowed opacity-50 shadow-none grayscale hover:translate-y-0',\n    className,\n  )}\n  draggable={disabled ? false : true}\n  tabindex={disabled ? -1 : 0}\n  aria-roledescription={keyboardDraggable && !disabled ? 'draggable card' : undefined}\n  onclick={onClick}\n  onkeydown={onKeydown}\n  ondragstart={onDragStart}\n  ondragend={onDragEnd}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/BoardCard.svelte"
    },
    {
      "path": "packages/registry-svelte/components/board/board.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file so consuming SFCs avoid the\n * circular-dep trap when importing from `index.ts`. Same pattern as\n * `timeline.variants.ts` / `card.variants.ts`.\n */\n\nexport const boardLaneVariants = cva(\n  'flex min-h-0 flex-col gap-3 rounded-xl border p-3 motion-safe:transition-[background-color,border-color,box-shadow] motion-safe:duration-200 motion-safe:ease-[cubic-bezier(0.16,1,0.3,1)]',\n  {\n    variants: {\n      tone: {\n        default: 'bg-muted/30',\n        plain: 'bg-transparent',\n      },\n      // Visual weight of `over` and `rejecting` deliberately matches so\n      // the eye reads them as the same kind of signal (drop intent),\n      // differing only in tone (primary = OK, destructive = NO).\n      state: {\n        idle: '',\n        over: 'border-primary/60 bg-primary/5 ring-2 ring-primary/30 ring-offset-1 ring-offset-background',\n        rejecting:\n          'border-destructive/40 bg-destructive/5 ring-2 ring-destructive/30 ring-offset-1 ring-offset-background',\n      },\n    },\n    defaultVariants: { tone: 'default', state: 'idle' },\n  },\n)\n\nexport const boardCardVariants = cva(\n  'bg-card hover:border-primary/40 group block w-full cursor-grab rounded-md border p-2 text-left outline-none motion-safe:transition-[transform,box-shadow,opacity,border-color] motion-safe:duration-200 motion-safe:ease-[cubic-bezier(0.16,1,0.3,1)] focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-primary/30 active:cursor-grabbing',\n  {\n    variants: {\n      state: {\n        idle: 'shadow-sm motion-safe:hover:-translate-y-1 hover:shadow-md',\n        dragging: 'scale-95 opacity-40 shadow-none',\n        moved: 'shadow-sm ring-2 ring-primary/50 ring-offset-1 ring-offset-background',\n      },\n    },\n    defaultVariants: { state: 'idle' },\n  },\n)\n\nexport type BoardLaneVariantsProps = VariantProps<typeof boardLaneVariants>\nexport type BoardCardVariantsProps = VariantProps<typeof boardCardVariants>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/board.variants.ts"
    },
    {
      "path": "packages/registry-svelte/components/board/context.ts",
      "content": "import { getContext } from 'svelte'\n\nexport type BoardOrientation = 'horizontal' | 'vertical'\nexport type BoardDensity = 'compact' | 'default' | 'comfortable'\n\n/** Drop event emitted by board state helpers (see the `use-board` hook twin). */\nexport interface BoardDropEvent {\n  /** First (anchor) item moved. For multi-item drops, see `itemIds`. */\n  itemId: string\n  /** All items moved in this drop, in their final visual order. */\n  itemIds: string[]\n  from: string\n  to: string\n  /** Insertion index of the first item in the target lane. */\n  index: number\n}\n\n/** Predicate consumers pass to control which items each lane accepts. */\nexport type BoardAcceptsFn = (itemId: string, fromLaneId: string, toLaneId: string) => boolean\n\n/**\n * External state snapshot — mirrors React `useBoard`'s `state`\n * (`UseBoardState`). Pass the whole object as `<Board state={...}>` instead\n * of threading the five fields as individual props.\n */\nexport interface BoardState {\n  draggingId?: string | null\n  draggingIds?: readonly string[]\n  dragOverLaneId?: string | null\n  justMovedId?: string | null\n  selectedIds?: ReadonlySet<string>\n}\n\nexport interface BoardContext {\n  orientation: BoardOrientation\n  density: BoardDensity\n  /** Transition name for lane bodies. Defaults to `motion-list` (the @uipkge motion preset). */\n  motion: string\n  /** Currently-grabbed primary card id (pointer drag OR keyboard grab). */\n  draggingId: string | null\n  /** All cards being dragged this turn — usually `[draggingId]`, but\n   *  expands to the full selection when the user grabs one of a multi-\n   *  selected set. Lanes read this to compute drop math (the dragged\n   *  cards are excluded from the insertion-index walk). */\n  draggingIds: readonly string[]\n  /** Lane currently under the pointer/keyboard cursor. */\n  dragOverLaneId: string | null\n  /** Card that just landed — used by consumers for a momentary highlight. */\n  justMovedId: string | null\n  /** Multi-select set. Click-and-drag any selected card moves the whole\n   *  set; click a non-selected card to drag that one alone. */\n  selectedIds: ReadonlySet<string>\n  /** Predicate run by lanes; defaults to always-accept. */\n  accepts: BoardAcceptsFn\n  /** Imperative move — used by keyboard handlers + external callers. */\n  moveItem: (itemId: string | string[], toLaneId: string, toIndex?: number) => void\n  /** Toggle one item in the selection set (or replace it if `additive` is false). */\n  toggleSelection: (itemId: string, additive?: boolean) => void\n  /** Drop the entire selection. */\n  clearSelection: () => void\n  /** Per-card allow-list registry. BoardCard registers its own `allowedLanes`\n   *  on mount; lanes consult this to short-circuit rejected drops without\n   *  the consumer having to encode the rule inside `accepts`. */\n  registerAllowedLanes: (cardId: string, lanes: readonly string[] | undefined) => void\n  unregisterAllowedLanes: (cardId: string) => void\n  /** Whether the lane's drops are accepted for the dragging item, given\n   *  the current `accepts` + per-card allowedLanes + lane disabled state. */\n  isLaneAcceptingFor: (laneId: string) => boolean\n  /** Lane registration (mirrors Timeline pattern). */\n  laneIds: symbol[]\n  registerLane: (id: symbol) => void\n  unregisterLane: (id: symbol) => void\n  /** Lane disabled registry — lanes call register/unregister; board state\n   *  helpers + isLaneAcceptingFor read from it. */\n  registerLaneDisabled: (laneId: string, disabled: boolean) => void\n  unregisterLaneDisabled: (laneId: string) => void\n}\n\nexport const BOARD_CONTEXT = Symbol('BoardContext')\n\n/** React-parity alias: React names this interface `BoardContextValue`. */\nexport type BoardContextValue = BoardContext\n\nexport interface BoardLaneContext {\n  laneId: string\n  isDragOver: boolean\n  isAccepting: boolean\n  disabled: boolean\n}\n\nexport const BOARD_LANE_CONTEXT = Symbol('BoardLaneContext')\n\n/** React-parity alias: React names this interface `BoardLaneContextValue`. */\nexport type BoardLaneContextValue = BoardLaneContext\n\nexport interface BoardCardContext {\n  cardId: string\n  laneId: string\n  isDragging: boolean\n  isJustMoved: boolean\n  isSelected: boolean\n  disabled: boolean\n}\n\nexport const BOARD_CARD_CONTEXT = Symbol('BoardCardContext')\n\n/** React-parity alias: React names this interface `BoardCardContextValue`. */\nexport type BoardCardContextValue = BoardCardContext\n\n// ---------------------------------------------------------------------------\n// Context helpers (Svelte-idiomatic `get*` twins of React's `useBoard*` hooks)\n// ---------------------------------------------------------------------------\n\n/** Read the board context. Throws outside `<Board>` — mirrors React's `useBoardContext`. */\nexport function getBoardContext(): BoardContext {\n  const ctx = getContext<BoardContext | null>(BOARD_CONTEXT)\n  if (!ctx) throw new Error('<BoardCard> / <BoardLane> must be a descendant of <Board>.')\n  return ctx\n}\n\n/** Read the lane context. Throws outside `<BoardLane>` — mirrors React's `useBoardLaneContext`. */\nexport function getBoardLaneContext(): BoardLaneContext {\n  const ctx = getContext<BoardLaneContext | null>(BOARD_LANE_CONTEXT)\n  if (!ctx) throw new Error('<BoardCard> must be a descendant of <BoardLane>.')\n  return ctx\n}\n\n/** Read the card context. Throws outside `<BoardCard>`. */\nexport function getBoardCardContext(): BoardCardContext {\n  const ctx = getContext<BoardCardContext | null>(BOARD_CARD_CONTEXT)\n  if (!ctx) throw new Error('Board card parts must be a descendant of <BoardCard>.')\n  return ctx\n}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/context.ts"
    },
    {
      "path": "packages/registry-svelte/components/board/index.ts",
      "content": "export { default as Board, type BoardProps } from './Board.svelte'\nexport { default as BoardLane, type BoardLaneProps, type BoardLaneSlotProps } from './BoardLane.svelte'\nexport { default as BoardLaneHeader, type BoardLaneHeaderProps } from './BoardLaneHeader.svelte'\nexport { default as BoardLaneBody, type BoardLaneBodyProps } from './BoardLaneBody.svelte'\nexport { default as BoardLaneEmpty, type BoardLaneEmptyProps } from './BoardLaneEmpty.svelte'\nexport { default as BoardCard, type BoardCardProps } from './BoardCard.svelte'\n\nexport {\n  BOARD_CONTEXT,\n  BOARD_LANE_CONTEXT,\n  BOARD_CARD_CONTEXT,\n  getBoardContext,\n  getBoardLaneContext,\n  getBoardCardContext,\n  type BoardAcceptsFn,\n  type BoardContext,\n  type BoardContextValue,\n  type BoardCardContext,\n  type BoardCardContextValue,\n  type BoardDensity,\n  type BoardDropEvent,\n  type BoardLaneContext,\n  type BoardLaneContextValue,\n  type BoardOrientation,\n  type BoardState,\n} from './context'\n\nexport {\n  boardCardVariants,\n  boardLaneVariants,\n  type BoardCardVariantsProps,\n  type BoardLaneVariantsProps,\n} from './board.variants'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/board/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Six small composable components for any board / kanban / sortable-list surface — opinionated about drop targeting and animation, agnostic about layout, data, and chrome. Drop `<Board>` around a grid of `<BoardLane>` columns; slot `<BoardLaneHeader>`, `<BoardLaneBody>`, `<BoardLaneEmpty>`, and `<BoardCard>` items inside each lane. State lives in a sibling board state helper (insertion-index drop math, keyboard a11y, accept predicate). Mirrors the Timeline / Card sub-component pattern (three-level context: board → lane → card).",
  "categories": [
    "data",
    "layout"
  ]
}