PhiaUi.Components.KanbanBoard (phia_ui v0.1.17)

Copy Markdown View Source

Kanban board component for PhiaUI.

Renders a horizontal workflow board composed of named columns and draggable task cards. The layout is CSS-only; drag-and-drop reordering is provided by the optional PhiaKanban vanilla-JS hook.

When to use

Use KanbanBoard whenever you need a multi-stage workflow visualisation with discrete status columns — project management, recruitment pipelines, support ticket queues, content-approval workflows, etc.

Anatomy

ComponentElementPurpose
kanban_board/1divHorizontal scroll container for all columns
kanban_column/1divA single workflow stage (label + optional count)
kanban_card/1liAn individual task card with slots

Priority levels

ValueLeft-border colour
criticalborder-l-destructive (red)
highborder-l-orange-500 (orange)
mediumborder-l-yellow-500 (yellow)
lowborder-l-muted-foreground/40

Minimal example

<.kanban_board id="project-board">
  <.kanban_column id="col-todo" label="To Do" count={length(@todo_cards)}>
    <.kanban_card
      :for={card <- @todo_cards}
      id={"card-#{card.id}"}
      title={card.title}
      priority={card.priority}
    />
  </.kanban_column>
</.kanban_board>

Full project-management board with LiveView streams

LiveView streams keep DOM diffing minimal — only changed cards are patched, not the entire column.

<.kanban_board id="pm-board" phx-hook="PhiaKanban" data-on-move="card_moved">
  <.kanban_column id="col-backlog" label="Backlog" count={@backlog_count}>
    <.kanban_card
      :for={{dom_id, card} <- @streams.backlog}
      id={dom_id}
      title={card.title}
      priority={card.priority}
      phx-click="open_card"
      phx-value-id={card.id}
    >
      <:avatar>
        <.avatar size="xs">
          <.avatar_image src={card.assignee_avatar} />
          <.avatar_fallback name={card.assignee_name} />
        </.avatar>
      </:avatar>
      <:tags>
        <.badge :for={label <- card.labels} variant="secondary" size="sm">
          {label}
        </.badge>
      </:tags>
      <:footer>
        <.icon name="calendar" size={:xs} />
        <span>{card.due_date}</span>
        <.icon name="message-square" size={:xs} class="ml-auto" />
        <span>{card.comment_count}</span>
      </:footer>
    </.kanban_card>
  </.kanban_column>

  <.kanban_column id="col-in-progress" label="In Progress" count={@in_progress_count}>
    <%!-- ... --%>
  </.kanban_column>

  <.kanban_column id="col-done" label="Done" count={@done_count}>
    <%!-- ... --%>
  </.kanban_column>
</.kanban_board>

Drag-and-drop setup (optional)

The board works without the hook — cards are static server-rendered items. Add phx-hook="PhiaKanban" to kanban_board/1 to enable drag-and-drop reordering. The hook emits a push_event("phx:card_moved", %{id, from, to, index}) on every successful drop.

# app.js
import PhiaKanban from "./hooks/kanban"
let liveSocket = new LiveSocket("/live", Socket, {
  hooks: { PhiaKanban }
})

# LiveView handler
def handle_event("card_moved", %{"id" => id, "from" => from, "to" => to, "index" => idx}, socket) do
  # Update the card's column and position in your database
  {:noreply, socket}
end

Summary

Functions

Renders the kanban board root container.

Renders a single kanban task card.

Renders a single kanban workflow column.

Functions

kanban_board(assigns)

Renders the kanban board root container.

All kanban_column/1 components go inside. The board overflows horizontally on narrow screens so every column remains accessible without horizontal viewport scrolling that would break the page layout.

Example

<.kanban_board id="sales-pipeline">
  <.kanban_column id="col-lead" label="Lead" count={12}>
    ...
  </.kanban_column>
  <.kanban_column id="col-qualified" label="Qualified" count={5}>
    ...
  </.kanban_column>
</.kanban_board>

Attributes

  • id (:string) (required) - DOM id for the board element. Required for LiveView streams and the PhiaKanban hook.
  • class (:string) - Additional CSS classes for the board container. Defaults to nil.
  • Global attributes are accepted. HTML attributes forwarded to the root element. Use phx-hook="PhiaKanban" to enable drag-and-drop reordering. Use data-on-move to specify the LiveView event name for card moves. Supports all globals plus: ["phx-hook", "data-on-move"].

Slots

kanban_card(assigns)

Renders a single kanban task card.

Cards are <li> elements inside the column's <ol>, which preserves correct document semantics for ordered task lists.

The priority-coloured left border gives a quick visual signal without requiring team members to read each card individually.

Example — full card with all slots

<.kanban_card
  id={"card-#{task.id}"}
  title={task.title}
  priority={task.priority}
  phx-click="open_task"
  phx-value-id={task.id}
>
  <:avatar>
    <.avatar size="xs">
      <.avatar_image src={task.assignee.avatar_url} />
      <.avatar_fallback name={task.assignee.name} />
    </.avatar>
  </:avatar>
  <:tags>
    <.badge :for={label <- task.labels} variant="secondary" size="sm">
      {label.name}
    </.badge>
  </:tags>
  <:footer>
    <.icon name="calendar" size={:xs} />
    <span>Due {task.due_date}</span>
  </:footer>
</.kanban_card>

Attributes

  • id (:string) (required) - DOM id for the card element. Required for LiveView streams (dom_id from stream).

  • title (:string) (required) - Card title — the task or item name.

  • priority (:string) - Priority level. Controls the left-border accent colour:

    • critical — red (destructive)
    • high — orange
    • medium — yellow
    • low — muted

    Defaults to "medium". Must be one of "critical", "high", "medium", or "low".

  • class (:string) - Additional CSS classes for the card. Defaults to nil.

  • Global attributes are accepted. HTML attributes forwarded to the <li> card element. Add phx-click="open_card" phx-value-id={card.id} to make cards clickable. The PhiaKanban hook sets draggable="true" automatically when enabled. Supports all globals plus: ["phx-click", "phx-value-id", "draggable"].

Slots

  • avatar - Optional avatar or avatar_group/1 for assignees. Rendered in the top-right of the card header.

  • tags - Optional tag / badge row rendered below the title. Use badge/1 components with variant="secondary".

  • footer - Optional footer row for metadata such as due dates, comment counts, or story-point estimates. Content is rendered at text-xs text-muted-foreground.

kanban_column(assigns)

Renders a single kanban workflow column.

Each column has a fixed width of w-64 (256 px) and does not shrink, which ensures consistent column widths regardless of card content. The count badge helps teams see at a glance whether a stage has become a bottleneck.

Example

<.kanban_column id="col-review" label="In Review" count={@review_count}>
  <.kanban_card
    :for={{dom_id, card} <- @streams.review}
    id={dom_id}
    title={card.title}
    priority={card.priority}
  />
</.kanban_column>

Attributes

  • id (:string) (required) - DOM id for the column. Required for the PhiaKanban hook to identify drop zones.

  • label (:string) (required) - Column heading for the workflow stage, e.g. "To Do", "In Progress", "Done".

  • count (:integer) - Optional item count displayed as a badge beside the label. Useful for at-a-glance capacity monitoring. Pass nil to omit the badge.

    Defaults to nil.

  • class (:string) - Additional CSS classes for the column wrapper. Defaults to nil.

  • Global attributes are accepted. HTML attributes forwarded to the column div (e.g. data-column-id for the hook).

Slots