# Board column footers

> A board nested under a page section: column headings start at level 4, and each column ends with a footer snippet that adds a card and shows the count.

Language: en
Canonical: https://texuli.com/examples/data-board-footer/
Markdown: https://texuli.com/examples/data-board-footer.md (this page). Every texuli.com page has a Markdown twin at its URL plus `.md` (the home page is https://texuli.com/index.md), and any page URL answers `Accept: text/markdown` with its twin instead of HTML.
Last Updated: 2026-11-13

---

[All examples](https://texuli.com/examples/)

A board nested under a page section: column headings start at level 4, and each column ends with a footer snippet that adds a card and shows the count.

[Board column footers](https://texuli.com/examples/data-board-footer/)

`data-board-footer` · `data/board`

 data board

 Props
- `columnFooter`
- `headingLevel`

Live example loads in an isolated same-origin preview frame.

Source (data-board-footer/DataBoardFooterDemo.svelte)

```
<script lang="ts">
import { Board, type BoardMove, rankBetween } from '@texuli/data/board';

interface Ticket {
  id: string;
  title: string;
  column: string;
  rank: string;
}

const columns = [
  { id: 'new', title: 'New' },
  { id: 'triaged', title: 'Triaged' },
  { id: 'closed', title: 'Closed' },
];
// Fictional data only.
let tickets = $state<Ticket[]>([
  { id: 't1', title: 'Login link expired', column: 'new', rank: 'a0' },
  { id: 't2', title: 'Export is slow', column: 'triaged', rank: 'a0' },
  { id: 't3', title: 'Typo on pricing', column: 'closed', rank: 'a0' },
]);
let created = 0;

function onMove(move: BoardMove): void {
  tickets = tickets.map((ticket) =>
    ticket.id === move.itemId ? { ...ticket, column: move.columnId, rank: move.rank } : ticket
  );
}

function add(columnId: string): void {
  const last = tickets
    .filter((ticket) => ticket.column === columnId)
    .map((ticket) => ticket.rank)
    .sort()
    .at(-1);
  created += 1;
  tickets = [
    ...tickets,
    {
      id: `new-${created}`,
      title: `New ticket ${created}`,
      column: columnId,
      rank: rankBetween(last ?? null, null),
    },
  ];
}
</script>

<section class="demo" data-example="data-board-footer" aria-labelledby="data-board-footer-title">
  <h2 id="data-board-footer-title">Support queue</h2>
  <section aria-labelledby="data-board-footer-sub">
    <h3 id="data-board-footer-sub">This week</h3>
    <Board
      {columns}
      items={tickets}
      headingLevel={4}
      getId={(ticket) => ticket.id}
      getColumn={(ticket) => ticket.column}
      getRank={(ticket) => ticket.rank}
      getLabel={(ticket) => ticket.title}
      {onMove}
      labels={{ board: 'Support queue columns' }}
    >
      {#snippet card(ticket)}
        <strong>{ticket.title}</strong>
      {/snippet}
      {#snippet columnFooter(column, { count })}
        <button type="button" class="demo-add" onclick={() => add(column.id)}>
          Add to {column.title}
        </button>
        <span class="demo-count">{count} {count === 1 ? 'ticket' : 'tickets'}</span>
      {/snippet}
    </Board>
  </section>
</section>

<style>
  .demo {
    display: grid;
    gap: 0.75rem;
    max-inline-size: 100%;
    padding: 1.5rem;
    color: var(--tx-foreground, CanvasText);
  }
  /* A grid item's automatic minimum is its content's min-content width (the board's
     columns): without this the board's section, and the page, grew on a phone. */
  .demo > * {
    min-inline-size: 0;
  }
  .demo h2,
  .demo h3 {
    margin: 0 0 0.5rem;
  }
  .demo-add {
    min-block-size: 2.25rem;
    padding-inline: 0.75rem;
    border: 1px dashed var(--tx-border-strong, ButtonBorder);
    border-radius: var(--tx-radius, 0.5rem);
    background: var(--tx-surface, ButtonFace);
    color: var(--tx-surface-foreground, ButtonText);
    font: inherit;
  }
  .demo-count {
    display: block;
    margin-block-start: 0.375rem;
    font-size: 0.875rem;
  }
</style>
```

## For agents

A worked example rendered live in the browser; the isolated preview frame (`/examples/preview/<id>/`) shows the same example without the site shell, and the example page next to it carries its code. The live parts run in the browser and are not in this Markdown; the operations below do the same work through the read-only MCP server at https://texuli.com/mcp (no sign-in; see https://texuli.com/auth.md).

MCP operations ([reference](https://github.com/DailybotHQ/texuli/blob/main/packages/tools/docs/ops.md#operations)):

- `example.search`: Find component examples.
- `component.get`: Read a component manifest.

Docs:

- [Component docs for agents (llms.txt)](https://texuli.com/llms/llms.txt)
- [Catalog manifest snapshot (JSON)](https://texuli.com/catalog/catalog.json)
- [@texuli/examples README](https://github.com/DailybotHQ/texuli/blob/main/packages/examples/README.md)
- [texuli README](https://github.com/DailybotHQ/texuli/blob/main/packages/texuli/README.md)
