Table

Styles an authored <table> in place: write the caption, thead, tbody, and th scope yourself, and the table reads as a table before JavaScript and to a screen reader. A cell's data-ui-align (start, center, or end; start when unset) sets its content along the row, such as end for a column of figures; compose a value with a muted line beneath it from Text. Table is presentation only: it does not sort, page, or select rows.

Recent updates

A compact two-column table with readable headings and two rows.

Recent updates
PageStatus
RoadmapDraft
GuideReady

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-table/ui-table.html"
>

<ui-table>
  <table>
    <caption>Recent updates</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Page</th>
        <th
          scope="col"
        >Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >Roadmap</th>
        <td>Draft</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Guide</th>
        <td>Ready</td>
      </tr>
    </tbody>
  </table>
</ui-table>
<script setup>
  import { UiTable } from "@threadlabs/looma";
</script>

<template>
  <UiTable>
    <table>
      <caption>Recent updates</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Page</th>
          <th
            scope="col"
          >Status</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >Roadmap</th>
          <td>Draft</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Guide</th>
          <td>Ready</td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</template>
import { UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Recent updates</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Page</th>
          <th
            scope="col"
          >Status</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >Roadmap</th>
          <td>Draft</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Guide</th>
          <td>Ready</td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
/** @jsxImportSource preact */
import { UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Recent updates</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Page</th>
          <th
            scope="col"
          >Status</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >Roadmap</th>
          <td>Draft</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Guide</th>
          <td>Ready</td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
<script>
  import { UiTable } from "@threadlabs/looma";
</script>

<UiTable>
  <table>
    <caption>Recent updates</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Page</th>
        <th
          scope="col"
        >Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >Roadmap</th>
        <td>Draft</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Guide</th>
        <td>Ready</td>
      </tr>
    </tbody>
  </table>
</UiTable>
import { Component } from "@angular/core";
import { UiTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable],
  template: `
    <div
      ui-table
    >
      <table>
        <caption>Recent updates</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Page</th>
            <th
              scope="col"
            >Status</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >Roadmap</th>
            <td>Draft</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Guide</th>
            <td>Ready</td>
          </tr>
        </tbody>
      </table>
    </div>
  `,
})
export default class Example {}

Default

An authored table, styled in place: a caption names it, th scope marks column and row headers, and data-ui-align="end" sets a column of figures at the end edge. A cell can hold a value with a muted line beneath it, composed from Text.

Recent orders
OrderCustomerStatusTotal

ORD-20931

Placed 2 April

Northwind Traders Paid $1,240.00

ORD-20932

Placed 3 April

Contoso Due $86.50

ORD-20933

Placed 3 April

Fabrikam Draft $12,000.00

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-table/ui-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-badge/ui-badge.html"
>

<ui-table>
  <table>
    <caption>Recent orders</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Order</th>
        <th
          scope="col"
        >Customer</th>
        <th
          scope="col"
        >Status</th>
        <th
          scope="col"
          data-ui-align="end"
        >Total</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >
          <ui-text
            variant="code"
          >ORD-20931</ui-text>
          <ui-text
            tone="muted"
            size="xs"
          >Placed 2 April</ui-text>
        </th>
        <td>Northwind Traders</td>
        <td>
          <ui-badge
            tone="success"
          >Paid</ui-badge>
        </td>
        <td
          data-ui-align="end"
        >$1,240.00</td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <ui-text
            variant="code"
          >ORD-20932</ui-text>
          <ui-text
            tone="muted"
            size="xs"
          >Placed 3 April</ui-text>
        </th>
        <td>Contoso</td>
        <td>
          <ui-badge
            tone="warning"
          >Due</ui-badge>
        </td>
        <td
          data-ui-align="end"
        >$86.50</td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <ui-text
            variant="code"
          >ORD-20933</ui-text>
          <ui-text
            tone="muted"
            size="xs"
          >Placed 3 April</ui-text>
        </th>
        <td>Fabrikam</td>
        <td>
          <ui-badge>Draft</ui-badge>
        </td>
        <td
          data-ui-align="end"
        >$12,000.00</td>
      </tr>
    </tbody>
  </table>
</ui-table>
<script setup>
  import { UiBadge, UiTable, UiText } from "@threadlabs/looma";
</script>

<template>
  <UiTable>
    <table>
      <caption>Recent orders</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Order</th>
          <th
            scope="col"
          >Customer</th>
          <th
            scope="col"
          >Status</th>
          <th
            scope="col"
            data-ui-align="end"
          >Total</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20931</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 2 April</UiText>
          </th>
          <td>Northwind Traders</td>
          <td>
            <UiBadge
              tone="success"
            >Paid</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$1,240.00</td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20932</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 3 April</UiText>
          </th>
          <td>Contoso</td>
          <td>
            <UiBadge
              tone="warning"
            >Due</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$86.50</td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20933</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 3 April</UiText>
          </th>
          <td>Fabrikam</td>
          <td>
            <UiBadge>Draft</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$12,000.00</td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</template>
import { UiBadge, UiTable, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Recent orders</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Order</th>
          <th
            scope="col"
          >Customer</th>
          <th
            scope="col"
          >Status</th>
          <th
            scope="col"
            data-ui-align="end"
          >Total</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20931</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 2 April</UiText>
          </th>
          <td>Northwind Traders</td>
          <td>
            <UiBadge
              tone="success"
            >Paid</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$1,240.00</td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20932</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 3 April</UiText>
          </th>
          <td>Contoso</td>
          <td>
            <UiBadge
              tone="warning"
            >Due</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$86.50</td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20933</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 3 April</UiText>
          </th>
          <td>Fabrikam</td>
          <td>
            <UiBadge>Draft</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$12,000.00</td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
/** @jsxImportSource preact */
import { UiBadge, UiTable, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Recent orders</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Order</th>
          <th
            scope="col"
          >Customer</th>
          <th
            scope="col"
          >Status</th>
          <th
            scope="col"
            data-ui-align="end"
          >Total</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20931</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 2 April</UiText>
          </th>
          <td>Northwind Traders</td>
          <td>
            <UiBadge
              tone="success"
            >Paid</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$1,240.00</td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20932</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 3 April</UiText>
          </th>
          <td>Contoso</td>
          <td>
            <UiBadge
              tone="warning"
            >Due</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$86.50</td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiText
              variant="code"
            >ORD-20933</UiText>
            <UiText
              tone="muted"
              size="xs"
            >Placed 3 April</UiText>
          </th>
          <td>Fabrikam</td>
          <td>
            <UiBadge>Draft</UiBadge>
          </td>
          <td
            data-ui-align="end"
          >$12,000.00</td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
<script>
  import { UiBadge, UiTable, UiText } from "@threadlabs/looma";
</script>

<UiTable>
  <table>
    <caption>Recent orders</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Order</th>
        <th
          scope="col"
        >Customer</th>
        <th
          scope="col"
        >Status</th>
        <th
          scope="col"
          data-ui-align="end"
        >Total</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >
          <UiText
            variant="code"
          >ORD-20931</UiText>
          <UiText
            tone="muted"
            size="xs"
          >Placed 2 April</UiText>
        </th>
        <td>Northwind Traders</td>
        <td>
          <UiBadge
            tone="success"
          >Paid</UiBadge>
        </td>
        <td
          data-ui-align="end"
        >$1,240.00</td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <UiText
            variant="code"
          >ORD-20932</UiText>
          <UiText
            tone="muted"
            size="xs"
          >Placed 3 April</UiText>
        </th>
        <td>Contoso</td>
        <td>
          <UiBadge
            tone="warning"
          >Due</UiBadge>
        </td>
        <td
          data-ui-align="end"
        >$86.50</td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <UiText
            variant="code"
          >ORD-20933</UiText>
          <UiText
            tone="muted"
            size="xs"
          >Placed 3 April</UiText>
        </th>
        <td>Fabrikam</td>
        <td>
          <UiBadge>Draft</UiBadge>
        </td>
        <td
          data-ui-align="end"
        >$12,000.00</td>
      </tr>
    </tbody>
  </table>
</UiTable>
import { Component } from "@angular/core";
import { UiBadge, UiTable, UiText } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable, UiText, UiBadge],
  template: `
    <div
      ui-table
    >
      <table>
        <caption>Recent orders</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Order</th>
            <th
              scope="col"
            >Customer</th>
            <th
              scope="col"
            >Status</th>
            <th
              scope="col"
              data-ui-align="end"
            >Total</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >
              <ng-template
                ui-text
                [variant]="'code'"
              >ORD-20931</ng-template>
              <ng-template
                ui-text
                [tone]="'muted'"
                [size]="'xs'"
              >Placed 2 April</ng-template>
            </th>
            <td>Northwind Traders</td>
            <td>
              <span
                ui-badge
                [tone]="'success'"
              >Paid</span>
            </td>
            <td
              data-ui-align="end"
            >$1,240.00</td>
          </tr>
          <tr>
            <th
              scope="row"
            >
              <ng-template
                ui-text
                [variant]="'code'"
              >ORD-20932</ng-template>
              <ng-template
                ui-text
                [tone]="'muted'"
                [size]="'xs'"
              >Placed 3 April</ng-template>
            </th>
            <td>Contoso</td>
            <td>
              <span
                ui-badge
                [tone]="'warning'"
              >Due</span>
            </td>
            <td
              data-ui-align="end"
            >$86.50</td>
          </tr>
          <tr>
            <th
              scope="row"
            >
              <ng-template
                ui-text
                [variant]="'code'"
              >ORD-20933</ng-template>
              <ng-template
                ui-text
                [tone]="'muted'"
                [size]="'xs'"
              >Placed 3 April</ng-template>
            </th>
            <td>Fabrikam</td>
            <td>
              <span
                ui-badge
              >Draft</span>
            </td>
            <td
              data-ui-align="end"
            >$12,000.00</td>
          </tr>
        </tbody>
      </table>
    </div>
  `,
})
export default class Example {}

Compact, with controls

density="compact" sets shorter rows for long tables to scan. Every row is at least one control tall, so a row holding a checkbox or a small button keeps the rhythm of the rows around it.

Team members
NameRoleSeatsActions
Owner3
Editor1
Viewer1

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-table/ui-table.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-checkbox/ui-checkbox.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-table
  density="compact"
>
  <table>
    <caption>Team members</caption>
    <thead>
      <tr>
        <th
          scope="col"
        ><span
            style="padding-inline-start: calc(1.125rem + var(--ui-space-2))"
          >Name</span></th>
        <th
          scope="col"
        >Role</th>
        <th
          scope="col"
          data-ui-align="end"
        >Seats</th>
        <th
          scope="col"
          data-ui-align="center"
        >Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >
          <ui-checkbox
            checked
          >Ada Lovelace</ui-checkbox>
        </th>
        <td>Owner</td>
        <td
          data-ui-align="end"
        >3</td>
        <td
          data-ui-align="center"
        >
          <ui-button
            size="sm"
            variant="ghost"
            aria-label="Edit Ada Lovelace"
          >Edit</ui-button>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <ui-checkbox>Grace Hopper</ui-checkbox>
        </th>
        <td>Editor</td>
        <td
          data-ui-align="end"
        >1</td>
        <td
          data-ui-align="center"
        >
          <ui-button
            size="sm"
            variant="ghost"
            aria-label="Edit Grace Hopper"
          >Edit</ui-button>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <ui-checkbox>Alan Turing</ui-checkbox>
        </th>
        <td>Viewer</td>
        <td
          data-ui-align="end"
        >1</td>
        <td
          data-ui-align="center"
        >
          <ui-button
            size="sm"
            variant="ghost"
            aria-label="Edit Alan Turing"
          >Edit</ui-button>
        </td>
      </tr>
    </tbody>
  </table>
</ui-table>
<script setup>
  import { UiButton, UiCheckbox, UiTable } from "@threadlabs/looma";
</script>

<template>
  <UiTable
    density="compact"
  >
    <table>
      <caption>Team members</caption>
      <thead>
        <tr>
          <th
            scope="col"
          ><span
              style="padding-inline-start: calc(1.125rem + var(--ui-space-2))"
            >Name</span></th>
          <th
            scope="col"
          >Role</th>
          <th
            scope="col"
            data-ui-align="end"
          >Seats</th>
          <th
            scope="col"
            data-ui-align="center"
          >Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox
              :checked="true"
            >Ada Lovelace</UiCheckbox>
          </th>
          <td>Owner</td>
          <td
            data-ui-align="end"
          >3</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Ada Lovelace"
            >Edit</UiButton>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox>Grace Hopper</UiCheckbox>
          </th>
          <td>Editor</td>
          <td
            data-ui-align="end"
          >1</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Grace Hopper"
            >Edit</UiButton>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox>Alan Turing</UiCheckbox>
          </th>
          <td>Viewer</td>
          <td
            data-ui-align="end"
          >1</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Alan Turing"
            >Edit</UiButton>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</template>
import { UiButton, UiCheckbox, UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable
    density="compact"
  >
    <table>
      <caption>Team members</caption>
      <thead>
        <tr>
          <th
            scope="col"
          ><span
              style={{"paddingInlineStart":"calc(1.125rem + var(--ui-space-2))"}}
            >Name</span></th>
          <th
            scope="col"
          >Role</th>
          <th
            scope="col"
            data-ui-align="end"
          >Seats</th>
          <th
            scope="col"
            data-ui-align="center"
          >Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox
              checked
            >Ada Lovelace</UiCheckbox>
          </th>
          <td>Owner</td>
          <td
            data-ui-align="end"
          >3</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Ada Lovelace"
            >Edit</UiButton>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox>Grace Hopper</UiCheckbox>
          </th>
          <td>Editor</td>
          <td
            data-ui-align="end"
          >1</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Grace Hopper"
            >Edit</UiButton>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox>Alan Turing</UiCheckbox>
          </th>
          <td>Viewer</td>
          <td
            data-ui-align="end"
          >1</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Alan Turing"
            >Edit</UiButton>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
/** @jsxImportSource preact */
import { UiButton, UiCheckbox, UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable
    density="compact"
  >
    <table>
      <caption>Team members</caption>
      <thead>
        <tr>
          <th
            scope="col"
          ><span
              style={{"paddingInlineStart":"calc(1.125rem + var(--ui-space-2))"}}
            >Name</span></th>
          <th
            scope="col"
          >Role</th>
          <th
            scope="col"
            data-ui-align="end"
          >Seats</th>
          <th
            scope="col"
            data-ui-align="center"
          >Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox
              checked
            >Ada Lovelace</UiCheckbox>
          </th>
          <td>Owner</td>
          <td
            data-ui-align="end"
          >3</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Ada Lovelace"
            >Edit</UiButton>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox>Grace Hopper</UiCheckbox>
          </th>
          <td>Editor</td>
          <td
            data-ui-align="end"
          >1</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Grace Hopper"
            >Edit</UiButton>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >
            <UiCheckbox>Alan Turing</UiCheckbox>
          </th>
          <td>Viewer</td>
          <td
            data-ui-align="end"
          >1</td>
          <td
            data-ui-align="center"
          >
            <UiButton
              size="sm"
              variant="ghost"
              aria-label="Edit Alan Turing"
            >Edit</UiButton>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
<script>
  import { UiButton, UiCheckbox, UiTable } from "@threadlabs/looma";
</script>

<UiTable
  density="compact"
>
  <table>
    <caption>Team members</caption>
    <thead>
      <tr>
        <th
          scope="col"
        ><span
            style="padding-inline-start: calc(1.125rem + var(--ui-space-2))"
          >Name</span></th>
        <th
          scope="col"
        >Role</th>
        <th
          scope="col"
          data-ui-align="end"
        >Seats</th>
        <th
          scope="col"
          data-ui-align="center"
        >Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >
          <UiCheckbox
            checked
          >Ada Lovelace</UiCheckbox>
        </th>
        <td>Owner</td>
        <td
          data-ui-align="end"
        >3</td>
        <td
          data-ui-align="center"
        >
          <UiButton
            size="sm"
            variant="ghost"
            aria-label="Edit Ada Lovelace"
          >Edit</UiButton>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <UiCheckbox>Grace Hopper</UiCheckbox>
        </th>
        <td>Editor</td>
        <td
          data-ui-align="end"
        >1</td>
        <td
          data-ui-align="center"
        >
          <UiButton
            size="sm"
            variant="ghost"
            aria-label="Edit Grace Hopper"
          >Edit</UiButton>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >
          <UiCheckbox>Alan Turing</UiCheckbox>
        </th>
        <td>Viewer</td>
        <td
          data-ui-align="end"
        >1</td>
        <td
          data-ui-align="center"
        >
          <UiButton
            size="sm"
            variant="ghost"
            aria-label="Edit Alan Turing"
          >Edit</UiButton>
        </td>
      </tr>
    </tbody>
  </table>
</UiTable>
import { Component } from "@angular/core";
import { UiButton, UiCheckbox, UiTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable, UiCheckbox, UiButton],
  template: `
    <div
      ui-table
      [density]="'compact'"
    >
      <table>
        <caption>Team members</caption>
        <thead>
          <tr>
            <th
              scope="col"
            ><span
                style="padding-inline-start: calc(1.125rem + var(--ui-space-2))"
              >Name</span></th>
            <th
              scope="col"
            >Role</th>
            <th
              scope="col"
              data-ui-align="end"
            >Seats</th>
            <th
              scope="col"
              data-ui-align="center"
            >Actions</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >
              <label
                ui-checkbox
                [checked]="true"
              >Ada Lovelace</label>
            </th>
            <td>Owner</td>
            <td
              data-ui-align="end"
            >3</td>
            <td
              data-ui-align="center"
            >
              <ng-template
                ui-button
                [size]="'sm'"
                [variant]="'ghost'"
                [hnAttributes]="{ 'aria-label': 'Edit Ada Lovelace' }"
              >Edit</ng-template>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >
              <label
                ui-checkbox
              >Grace Hopper</label>
            </th>
            <td>Editor</td>
            <td
              data-ui-align="end"
            >1</td>
            <td
              data-ui-align="center"
            >
              <ng-template
                ui-button
                [size]="'sm'"
                [variant]="'ghost'"
                [hnAttributes]="{ 'aria-label': 'Edit Grace Hopper' }"
              >Edit</ng-template>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >
              <label
                ui-checkbox
              >Alan Turing</label>
            </th>
            <td>Viewer</td>
            <td
              data-ui-align="end"
            >1</td>
            <td
              data-ui-align="center"
            >
              <ng-template
                ui-button
                [size]="'sm'"
                [variant]="'ghost'"
                [hnAttributes]="{ 'aria-label': 'Edit Alan Turing' }"
              >Edit</ng-template>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  `,
})
export default class Example {}

Sticky header

sticky-header keeps the header row in view while the rows scroll beneath it. The table scrolls inside its own box, so it needs a bounded height.

Deliveries
DayParcels
Monday142
Tuesday98
Wednesday121
Thursday87
Friday163
Saturday54
Sunday12

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-table/ui-table.html"
>

<ui-table
  sticky-header
  style="block-size: 12rem"
>
  <table>
    <caption>Deliveries</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Day</th>
        <th
          scope="col"
          data-ui-align="end"
        >Parcels</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >Monday</th>
        <td
          data-ui-align="end"
        >142</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Tuesday</th>
        <td
          data-ui-align="end"
        >98</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Wednesday</th>
        <td
          data-ui-align="end"
        >121</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Thursday</th>
        <td
          data-ui-align="end"
        >87</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Friday</th>
        <td
          data-ui-align="end"
        >163</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Saturday</th>
        <td
          data-ui-align="end"
        >54</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Sunday</th>
        <td
          data-ui-align="end"
        >12</td>
      </tr>
    </tbody>
  </table>
</ui-table>
<script setup>
  import { UiTable } from "@threadlabs/looma";
</script>

<template>
  <UiTable
    :sticky-header="true"
    style="block-size: 12rem"
  >
    <table>
      <caption>Deliveries</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Day</th>
          <th
            scope="col"
            data-ui-align="end"
          >Parcels</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >Monday</th>
          <td
            data-ui-align="end"
          >142</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Tuesday</th>
          <td
            data-ui-align="end"
          >98</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Wednesday</th>
          <td
            data-ui-align="end"
          >121</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Thursday</th>
          <td
            data-ui-align="end"
          >87</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Friday</th>
          <td
            data-ui-align="end"
          >163</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Saturday</th>
          <td
            data-ui-align="end"
          >54</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Sunday</th>
          <td
            data-ui-align="end"
          >12</td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</template>
import { UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable
    stickyHeader
    style={{"blockSize":"12rem"}}
  >
    <table>
      <caption>Deliveries</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Day</th>
          <th
            scope="col"
            data-ui-align="end"
          >Parcels</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >Monday</th>
          <td
            data-ui-align="end"
          >142</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Tuesday</th>
          <td
            data-ui-align="end"
          >98</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Wednesday</th>
          <td
            data-ui-align="end"
          >121</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Thursday</th>
          <td
            data-ui-align="end"
          >87</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Friday</th>
          <td
            data-ui-align="end"
          >163</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Saturday</th>
          <td
            data-ui-align="end"
          >54</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Sunday</th>
          <td
            data-ui-align="end"
          >12</td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
/** @jsxImportSource preact */
import { UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable
    stickyHeader
    style={{"blockSize":"12rem"}}
  >
    <table>
      <caption>Deliveries</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Day</th>
          <th
            scope="col"
            data-ui-align="end"
          >Parcels</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >Monday</th>
          <td
            data-ui-align="end"
          >142</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Tuesday</th>
          <td
            data-ui-align="end"
          >98</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Wednesday</th>
          <td
            data-ui-align="end"
          >121</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Thursday</th>
          <td
            data-ui-align="end"
          >87</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Friday</th>
          <td
            data-ui-align="end"
          >163</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Saturday</th>
          <td
            data-ui-align="end"
          >54</td>
        </tr>
        <tr>
          <th
            scope="row"
          >Sunday</th>
          <td
            data-ui-align="end"
          >12</td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
<script>
  import { UiTable } from "@threadlabs/looma";
</script>

<UiTable
  stickyHeader
  style="block-size: 12rem"
>
  <table>
    <caption>Deliveries</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Day</th>
        <th
          scope="col"
          data-ui-align="end"
        >Parcels</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >Monday</th>
        <td
          data-ui-align="end"
        >142</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Tuesday</th>
        <td
          data-ui-align="end"
        >98</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Wednesday</th>
        <td
          data-ui-align="end"
        >121</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Thursday</th>
        <td
          data-ui-align="end"
        >87</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Friday</th>
        <td
          data-ui-align="end"
        >163</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Saturday</th>
        <td
          data-ui-align="end"
        >54</td>
      </tr>
      <tr>
        <th
          scope="row"
        >Sunday</th>
        <td
          data-ui-align="end"
        >12</td>
      </tr>
    </tbody>
  </table>
</UiTable>
import { Component } from "@angular/core";
import { UiTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable],
  template: `
    <div
      ui-table
      [stickyHeader]="true"
      style="block-size: 12rem"
    >
      <table>
        <caption>Deliveries</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Day</th>
            <th
              scope="col"
              data-ui-align="end"
            >Parcels</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >Monday</th>
            <td
              data-ui-align="end"
            >142</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Tuesday</th>
            <td
              data-ui-align="end"
            >98</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Wednesday</th>
            <td
              data-ui-align="end"
            >121</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Thursday</th>
            <td
              data-ui-align="end"
            >87</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Friday</th>
            <td
              data-ui-align="end"
            >163</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Saturday</th>
            <td
              data-ui-align="end"
            >54</td>
          </tr>
          <tr>
            <th
              scope="row"
            >Sunday</th>
            <td
              data-ui-align="end"
            >12</td>
          </tr>
        </tbody>
      </table>
    </div>
  `,
})
export default class Example {}

Wider than its container

A table wider than its container scrolls sideways and fades the edge that hides columns. While it scrolls, the table is a region named by its caption that the keyboard can focus and scroll with the arrow keys.

Quarterly figures
RegionQ1Q2Q3Q4Year
North1,2041,3881,5221,6105,724
South9861,0401,1151,3024,443

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-table/ui-table.html"
>

<div
  style="max-inline-size: 16rem"
>
  <ui-table>
    <table>
      <caption>Quarterly figures</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Region</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q1</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q2</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q3</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q4</th>
          <th
            scope="col"
            data-ui-align="end"
          >Year</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >North</th>
          <td
            data-ui-align="end"
          >1,204</td>
          <td
            data-ui-align="end"
          >1,388</td>
          <td
            data-ui-align="end"
          >1,522</td>
          <td
            data-ui-align="end"
          >1,610</td>
          <td
            data-ui-align="end"
          >5,724</td>
        </tr>
        <tr>
          <th
            scope="row"
          >South</th>
          <td
            data-ui-align="end"
          >986</td>
          <td
            data-ui-align="end"
          >1,040</td>
          <td
            data-ui-align="end"
          >1,115</td>
          <td
            data-ui-align="end"
          >1,302</td>
          <td
            data-ui-align="end"
          >4,443</td>
        </tr>
      </tbody>
    </table>
  </ui-table>
</div>
<script setup>
  import { UiTable } from "@threadlabs/looma";
</script>

<template>
  <div
    style="max-inline-size: 16rem"
  >
    <UiTable>
      <table>
        <caption>Quarterly figures</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Region</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q1</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q2</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q3</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q4</th>
            <th
              scope="col"
              data-ui-align="end"
            >Year</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >North</th>
            <td
              data-ui-align="end"
            >1,204</td>
            <td
              data-ui-align="end"
            >1,388</td>
            <td
              data-ui-align="end"
            >1,522</td>
            <td
              data-ui-align="end"
            >1,610</td>
            <td
              data-ui-align="end"
            >5,724</td>
          </tr>
          <tr>
            <th
              scope="row"
            >South</th>
            <td
              data-ui-align="end"
            >986</td>
            <td
              data-ui-align="end"
            >1,040</td>
            <td
              data-ui-align="end"
            >1,115</td>
            <td
              data-ui-align="end"
            >1,302</td>
            <td
              data-ui-align="end"
            >4,443</td>
          </tr>
        </tbody>
      </table>
    </UiTable>
  </div>
</template>
import { UiTable } from "@threadlabs/looma";

export default function Example() {
  return <div
    style={{"maxInlineSize":"16rem"}}
  >
    <UiTable>
      <table>
        <caption>Quarterly figures</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Region</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q1</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q2</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q3</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q4</th>
            <th
              scope="col"
              data-ui-align="end"
            >Year</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >North</th>
            <td
              data-ui-align="end"
            >1,204</td>
            <td
              data-ui-align="end"
            >1,388</td>
            <td
              data-ui-align="end"
            >1,522</td>
            <td
              data-ui-align="end"
            >1,610</td>
            <td
              data-ui-align="end"
            >5,724</td>
          </tr>
          <tr>
            <th
              scope="row"
            >South</th>
            <td
              data-ui-align="end"
            >986</td>
            <td
              data-ui-align="end"
            >1,040</td>
            <td
              data-ui-align="end"
            >1,115</td>
            <td
              data-ui-align="end"
            >1,302</td>
            <td
              data-ui-align="end"
            >4,443</td>
          </tr>
        </tbody>
      </table>
    </UiTable>
  </div>;
}
/** @jsxImportSource preact */
import { UiTable } from "@threadlabs/looma";

export default function Example() {
  return <div
    style={{"maxInlineSize":"16rem"}}
  >
    <UiTable>
      <table>
        <caption>Quarterly figures</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Region</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q1</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q2</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q3</th>
            <th
              scope="col"
              data-ui-align="end"
            >Q4</th>
            <th
              scope="col"
              data-ui-align="end"
            >Year</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >North</th>
            <td
              data-ui-align="end"
            >1,204</td>
            <td
              data-ui-align="end"
            >1,388</td>
            <td
              data-ui-align="end"
            >1,522</td>
            <td
              data-ui-align="end"
            >1,610</td>
            <td
              data-ui-align="end"
            >5,724</td>
          </tr>
          <tr>
            <th
              scope="row"
            >South</th>
            <td
              data-ui-align="end"
            >986</td>
            <td
              data-ui-align="end"
            >1,040</td>
            <td
              data-ui-align="end"
            >1,115</td>
            <td
              data-ui-align="end"
            >1,302</td>
            <td
              data-ui-align="end"
            >4,443</td>
          </tr>
        </tbody>
      </table>
    </UiTable>
  </div>;
}
<script>
  import { UiTable } from "@threadlabs/looma";
</script>

<div
  style="max-inline-size: 16rem"
>
  <UiTable>
    <table>
      <caption>Quarterly figures</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Region</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q1</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q2</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q3</th>
          <th
            scope="col"
            data-ui-align="end"
          >Q4</th>
          <th
            scope="col"
            data-ui-align="end"
          >Year</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >North</th>
          <td
            data-ui-align="end"
          >1,204</td>
          <td
            data-ui-align="end"
          >1,388</td>
          <td
            data-ui-align="end"
          >1,522</td>
          <td
            data-ui-align="end"
          >1,610</td>
          <td
            data-ui-align="end"
          >5,724</td>
        </tr>
        <tr>
          <th
            scope="row"
          >South</th>
          <td
            data-ui-align="end"
          >986</td>
          <td
            data-ui-align="end"
          >1,040</td>
          <td
            data-ui-align="end"
          >1,115</td>
          <td
            data-ui-align="end"
          >1,302</td>
          <td
            data-ui-align="end"
          >4,443</td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</div>
import { Component } from "@angular/core";
import { UiTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable],
  template: `
    <div
      style="max-inline-size: 16rem"
    >
      <div
        ui-table
      >
        <table>
          <caption>Quarterly figures</caption>
          <thead>
            <tr>
              <th
                scope="col"
              >Region</th>
              <th
                scope="col"
                data-ui-align="end"
              >Q1</th>
              <th
                scope="col"
                data-ui-align="end"
              >Q2</th>
              <th
                scope="col"
                data-ui-align="end"
              >Q3</th>
              <th
                scope="col"
                data-ui-align="end"
              >Q4</th>
              <th
                scope="col"
                data-ui-align="end"
              >Year</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <th
                scope="row"
              >North</th>
              <td
                data-ui-align="end"
              >1,204</td>
              <td
                data-ui-align="end"
              >1,388</td>
              <td
                data-ui-align="end"
              >1,522</td>
              <td
                data-ui-align="end"
              >1,610</td>
              <td
                data-ui-align="end"
              >5,724</td>
            </tr>
            <tr>
              <th
                scope="row"
              >South</th>
              <td
                data-ui-align="end"
              >986</td>
              <td
                data-ui-align="end"
              >1,040</td>
              <td
                data-ui-align="end"
              >1,115</td>
              <td
                data-ui-align="end"
              >1,302</td>
              <td
                data-ui-align="end"
              >4,443</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: div

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
densityHow tall each row is. Every row is at least one control tall, so a row holding a control lines up with the rows around it. comfortable fits a control of the standard height (2.5rem), for tables read or edited at a glance; compact fits a small one (2rem), such as a Checkbox or a Button with size="sm", for long tables to scan.densitykeywordcomfortablecomfortable, compact
sticky-headerKeeps the header row in view while the rows scroll beneath it. The table scrolls inside its own box, so this needs a bounded height, such as a table that fills a fixed-height panel.stickyHeaderbooleanfalse—

Framework props

NameTypeDefaultAllowed values
density"comfortable" | "compact"comfortablecomfortable, compact
stickyHeaderbooleanfalse—

Events

No events.

Slots

NamePurpose
defaultDefault child content.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

No component tokens.

Shared tokens

TokenFallback
--ui-border-divider—
--ui-border-subtle—
--ui-border-width—
--ui-control-size—
--ui-control-size-sm—
--ui-focus-ring—
--ui-focus-width—
--ui-font-medium—
--ui-font-size-sm—
--ui-line-height—
--ui-scroll-fade-size24px
--ui-space-0-5—
--ui-space-1—
--ui-space-2—
--ui-space-3—
--ui-surface—
--ui-text-muted—
--ui-text-primary—
--ui-text-secondary—