Meter

How far along something is, at a glance: the share of an invoice collected, of items reviewed, of a checklist done. value runs from 0 to max (1 by default, as on a native <meter>); a value outside that range draws at the nearer end, so an empty meter still shows its track and a full one fills it. The bar is drawn as the page renders, on the server and before JavaScript.

Default

A meter shows how far along something is. value runs from 0 to max (1 by default); label names it for assistive technology, which reads the percentage unless valueText says more.

Code

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

<ui-meter
  value="0.6"
  label="Uploaded"
></ui-meter>
<script setup>
  import { UiMeter } from "@threadlabs/looma";
</script>

<template>
  <UiMeter
    :value="0.6"
    label="Uploaded"
  />
</template>
import { UiMeter } from "@threadlabs/looma";

export default function Example() {
  return <UiMeter
    value={0.6}
    label="Uploaded"
  />;
}
/** @jsxImportSource preact */
import { UiMeter } from "@threadlabs/looma";

export default function Example() {
  return <UiMeter
    value={0.6}
    label="Uploaded"
  />;
}
<script>
  import { UiMeter } from "@threadlabs/looma";
</script>

<UiMeter
  value={0.6}
  label="Uploaded"
/>
import { Component } from "@angular/core";
import { UiMeter } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiMeter],
  template: `
    <span
      ui-meter
      [value]="0.6"
      [label]="'Uploaded'"
    ></span>
  `,
})
export default class Example {}

tone

The fill's colour carries the state, such as nothing yet, partial, complete, or needs attention. Colour alone reaches no one using assistive technology or forced colours, so valueText, and the text beside the meter, say the state too.

Not started

Partial

Complete

Overdue

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-meter/ui-meter.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-text/ui-text.html"
>

<ui-stack
  gap="xs"
>
  <ui-cluster
    gap="s"
  >
    <ui-meter
      value="0"
      max="1240"
      label="Collected"
      value-text="Nothing collected yet, $0 of $1,240"
    ></ui-meter>
    <ui-text
      size="sm"
    >Not started</ui-text>
  </ui-cluster>
  <ui-cluster
    gap="s"
  >
    <ui-meter
      value="750"
      max="1240"
      tone="info"
      label="Collected"
      value-text="Partly collected, $750 of $1,240"
    ></ui-meter>
    <ui-text
      size="sm"
    >Partial</ui-text>
  </ui-cluster>
  <ui-cluster
    gap="s"
  >
    <ui-meter
      value="1240"
      max="1240"
      tone="success"
      label="Collected"
      value-text="Fully collected, $1,240 of $1,240"
    ></ui-meter>
    <ui-text
      size="sm"
    >Complete</ui-text>
  </ui-cluster>
  <ui-cluster
    gap="s"
  >
    <ui-meter
      value="200"
      max="1240"
      tone="warning"
      label="Collected"
      value-text="Overdue, $200 of $1,240 collected"
    ></ui-meter>
    <ui-text
      size="sm"
    >Overdue</ui-text>
  </ui-cluster>
  <ui-meter
    value="65"
    tone="accent-secondary"
    label="Collection progress"
  ></ui-meter>
  <ui-meter
    value="35"
    tone="accent-tertiary"
    label="Related collection progress"
  ></ui-meter>
</ui-stack>
<script setup>
  import { UiCluster, UiMeter, UiStack, UiText } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="xs"
  >
    <UiCluster
      gap="s"
    >
      <UiMeter
        :value="0"
        :max="1240"
        label="Collected"
        value-text="Nothing collected yet, $0 of $1,240"
      />
      <UiText
        size="sm"
      >Not started</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        :value="750"
        :max="1240"
        tone="info"
        label="Collected"
        value-text="Partly collected, $750 of $1,240"
      />
      <UiText
        size="sm"
      >Partial</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        :value="1240"
        :max="1240"
        tone="success"
        label="Collected"
        value-text="Fully collected, $1,240 of $1,240"
      />
      <UiText
        size="sm"
      >Complete</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        :value="200"
        :max="1240"
        tone="warning"
        label="Collected"
        value-text="Overdue, $200 of $1,240 collected"
      />
      <UiText
        size="sm"
      >Overdue</UiText>
    </UiCluster>
    <UiMeter
      :value="65"
      tone="accent-secondary"
      label="Collection progress"
    />
    <UiMeter
      :value="35"
      tone="accent-tertiary"
      label="Related collection progress"
    />
  </UiStack>
</template>
import { UiCluster, UiMeter, UiStack, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="xs"
  >
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={0}
        max={1240}
        label="Collected"
        valueText="Nothing collected yet, $0 of $1,240"
      />
      <UiText
        size="sm"
      >Not started</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={750}
        max={1240}
        tone="info"
        label="Collected"
        valueText="Partly collected, $750 of $1,240"
      />
      <UiText
        size="sm"
      >Partial</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={1240}
        max={1240}
        tone="success"
        label="Collected"
        valueText="Fully collected, $1,240 of $1,240"
      />
      <UiText
        size="sm"
      >Complete</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={200}
        max={1240}
        tone="warning"
        label="Collected"
        valueText="Overdue, $200 of $1,240 collected"
      />
      <UiText
        size="sm"
      >Overdue</UiText>
    </UiCluster>
    <UiMeter
      value={65}
      tone="accent-secondary"
      label="Collection progress"
    />
    <UiMeter
      value={35}
      tone="accent-tertiary"
      label="Related collection progress"
    />
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiCluster, UiMeter, UiStack, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="xs"
  >
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={0}
        max={1240}
        label="Collected"
        valueText="Nothing collected yet, $0 of $1,240"
      />
      <UiText
        size="sm"
      >Not started</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={750}
        max={1240}
        tone="info"
        label="Collected"
        valueText="Partly collected, $750 of $1,240"
      />
      <UiText
        size="sm"
      >Partial</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={1240}
        max={1240}
        tone="success"
        label="Collected"
        valueText="Fully collected, $1,240 of $1,240"
      />
      <UiText
        size="sm"
      >Complete</UiText>
    </UiCluster>
    <UiCluster
      gap="s"
    >
      <UiMeter
        value={200}
        max={1240}
        tone="warning"
        label="Collected"
        valueText="Overdue, $200 of $1,240 collected"
      />
      <UiText
        size="sm"
      >Overdue</UiText>
    </UiCluster>
    <UiMeter
      value={65}
      tone="accent-secondary"
      label="Collection progress"
    />
    <UiMeter
      value={35}
      tone="accent-tertiary"
      label="Related collection progress"
    />
  </UiStack>;
}
<script>
  import { UiCluster, UiMeter, UiStack, UiText } from "@threadlabs/looma";
</script>

<UiStack
  gap="xs"
>
  <UiCluster
    gap="s"
  >
    <UiMeter
      value={0}
      max={1240}
      label="Collected"
      valueText="Nothing collected yet, $0 of $1,240"
    />
    <UiText
      size="sm"
    >Not started</UiText>
  </UiCluster>
  <UiCluster
    gap="s"
  >
    <UiMeter
      value={750}
      max={1240}
      tone="info"
      label="Collected"
      valueText="Partly collected, $750 of $1,240"
    />
    <UiText
      size="sm"
    >Partial</UiText>
  </UiCluster>
  <UiCluster
    gap="s"
  >
    <UiMeter
      value={1240}
      max={1240}
      tone="success"
      label="Collected"
      valueText="Fully collected, $1,240 of $1,240"
    />
    <UiText
      size="sm"
    >Complete</UiText>
  </UiCluster>
  <UiCluster
    gap="s"
  >
    <UiMeter
      value={200}
      max={1240}
      tone="warning"
      label="Collected"
      valueText="Overdue, $200 of $1,240 collected"
    />
    <UiText
      size="sm"
    >Overdue</UiText>
  </UiCluster>
  <UiMeter
    value={65}
    tone="accent-secondary"
    label="Collection progress"
  />
  <UiMeter
    value={35}
    tone="accent-tertiary"
    label="Related collection progress"
  />
</UiStack>
import { Component } from "@angular/core";
import { UiCluster, UiMeter, UiStack, UiText } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiCluster, UiMeter, UiText],
  template: `
    <div
      ui-stack
      [gap]="'xs'"
    >
      <div
        ui-cluster
        [gap]="'s'"
      >
        <span
          ui-meter
          [value]="0"
          [max]="1240"
          [label]="'Collected'"
          [valueText]="'Nothing collected yet, $0 of $1,240'"
        ></span>
        <ng-template
          ui-text
          [size]="'sm'"
        >Not started</ng-template>
      </div>
      <div
        ui-cluster
        [gap]="'s'"
      >
        <span
          ui-meter
          [value]="750"
          [max]="1240"
          [tone]="'info'"
          [label]="'Collected'"
          [valueText]="'Partly collected, $750 of $1,240'"
        ></span>
        <ng-template
          ui-text
          [size]="'sm'"
        >Partial</ng-template>
      </div>
      <div
        ui-cluster
        [gap]="'s'"
      >
        <span
          ui-meter
          [value]="1240"
          [max]="1240"
          [tone]="'success'"
          [label]="'Collected'"
          [valueText]="'Fully collected, $1,240 of $1,240'"
        ></span>
        <ng-template
          ui-text
          [size]="'sm'"
        >Complete</ng-template>
      </div>
      <div
        ui-cluster
        [gap]="'s'"
      >
        <span
          ui-meter
          [value]="200"
          [max]="1240"
          [tone]="'warning'"
          [label]="'Collected'"
          [valueText]="'Overdue, $200 of $1,240 collected'"
        ></span>
        <ng-template
          ui-text
          [size]="'sm'"
        >Overdue</ng-template>
      </div>
      <span
        ui-meter
        [value]="65"
        [tone]="'accent-secondary'"
        [label]="'Collection progress'"
      ></span>
      <span
        ui-meter
        [value]="35"
        [tone]="'accent-tertiary'"
        [label]="'Related collection progress'"
      ></span>
    </div>
  `,
})
export default class Example {}

In a table

size="sm" is a thin bar that sits in a line of text. Here it leads each amount in an end-aligned column, a nowrap Cluster keeps each bar beside its amount, the tone marks each invoice's state, and valueText reads the amounts and the state.

Invoices
InvoiceCollected
INV-1041
<ui-meter size="sm" value="0" max="1240" label="Collected" value-text="Nothing collected yet, $0 of $1,240"

$0.00

INV-1042
<ui-meter size="sm" value="750" max="1240" tone="info" label="Collected" value-text="Partly collected, $750 of $1,240"

$750.00

INV-1043
<ui-meter size="sm" value="86.5" max="86.5" tone="success" label="Collected" value-text="Fully collected, $86.50 of $86.50"

$86.50

INV-1044
<ui-meter size="sm" value="3000" max="12000" tone="warning" label="Collected" value-text="Overdue, $3,000 of $12,000 collected"

$3,000.00

Code

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

<ui-table>
  <table>
    <caption>Invoices</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Invoice</th>
        <th
          scope="col"
          data-ui-align="end"
        >Collected</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >INV-1041</th>
        <td
          data-ui-align="end"
        >
          <ui-cluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><ui-meter
              size="sm"
              value="0"
              max="1240"
              label="Collected"
              value-text="Nothing collected yet, $0 of $1,240"
            ></ui-meter>$0.00</ui-cluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >INV-1042</th>
        <td
          data-ui-align="end"
        >
          <ui-cluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><ui-meter
              size="sm"
              value="750"
              max="1240"
              tone="info"
              label="Collected"
              value-text="Partly collected, $750 of $1,240"
            ></ui-meter>$750.00</ui-cluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >INV-1043</th>
        <td
          data-ui-align="end"
        >
          <ui-cluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><ui-meter
              size="sm"
              value="86.5"
              max="86.5"
              tone="success"
              label="Collected"
              value-text="Fully collected, $86.50 of $86.50"
            ></ui-meter>$86.50</ui-cluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >INV-1044</th>
        <td
          data-ui-align="end"
        >
          <ui-cluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><ui-meter
              size="sm"
              value="3000"
              max="12000"
              tone="warning"
              label="Collected"
              value-text="Overdue, $3,000 of $12,000 collected"
            ></ui-meter>$3,000.00</ui-cluster>
        </td>
      </tr>
    </tbody>
  </table>
</ui-table>
<script setup>
  import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";
</script>

<template>
  <UiTable>
    <table>
      <caption>Invoices</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Invoice</th>
          <th
            scope="col"
            data-ui-align="end"
          >Collected</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >INV-1041</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                :value="0"
                :max="1240"
                label="Collected"
                value-text="Nothing collected yet, $0 of $1,240"
              />$0.00</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1042</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                :value="750"
                :max="1240"
                tone="info"
                label="Collected"
                value-text="Partly collected, $750 of $1,240"
              />$750.00</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1043</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                :value="86.5"
                :max="86.5"
                tone="success"
                label="Collected"
                value-text="Fully collected, $86.50 of $86.50"
              />$86.50</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1044</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                :value="3000"
                :max="12000"
                tone="warning"
                label="Collected"
                value-text="Overdue, $3,000 of $12,000 collected"
              />$3,000.00</UiCluster>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</template>
import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Invoices</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Invoice</th>
          <th
            scope="col"
            data-ui-align="end"
          >Collected</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >INV-1041</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={0}
                max={1240}
                label="Collected"
                valueText="Nothing collected yet, $0 of $1,240"
              />$0.00</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1042</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={750}
                max={1240}
                tone="info"
                label="Collected"
                valueText="Partly collected, $750 of $1,240"
              />$750.00</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1043</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={86.5}
                max={86.5}
                tone="success"
                label="Collected"
                valueText="Fully collected, $86.50 of $86.50"
              />$86.50</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1044</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={3000}
                max={12000}
                tone="warning"
                label="Collected"
                valueText="Overdue, $3,000 of $12,000 collected"
              />$3,000.00</UiCluster>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
/** @jsxImportSource preact */
import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Invoices</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Invoice</th>
          <th
            scope="col"
            data-ui-align="end"
          >Collected</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >INV-1041</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={0}
                max={1240}
                label="Collected"
                valueText="Nothing collected yet, $0 of $1,240"
              />$0.00</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1042</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={750}
                max={1240}
                tone="info"
                label="Collected"
                valueText="Partly collected, $750 of $1,240"
              />$750.00</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1043</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={86.5}
                max={86.5}
                tone="success"
                label="Collected"
                valueText="Fully collected, $86.50 of $86.50"
              />$86.50</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >INV-1044</th>
          <td
            data-ui-align="end"
          >
            <UiCluster
              gap="s"
              justify="end"
              wrap="nowrap"
            ><UiMeter
                size="sm"
                value={3000}
                max={12000}
                tone="warning"
                label="Collected"
                valueText="Overdue, $3,000 of $12,000 collected"
              />$3,000.00</UiCluster>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
<script>
  import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";
</script>

<UiTable>
  <table>
    <caption>Invoices</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Invoice</th>
        <th
          scope="col"
          data-ui-align="end"
        >Collected</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >INV-1041</th>
        <td
          data-ui-align="end"
        >
          <UiCluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><UiMeter
              size="sm"
              value={0}
              max={1240}
              label="Collected"
              valueText="Nothing collected yet, $0 of $1,240"
            />$0.00</UiCluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >INV-1042</th>
        <td
          data-ui-align="end"
        >
          <UiCluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><UiMeter
              size="sm"
              value={750}
              max={1240}
              tone="info"
              label="Collected"
              valueText="Partly collected, $750 of $1,240"
            />$750.00</UiCluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >INV-1043</th>
        <td
          data-ui-align="end"
        >
          <UiCluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><UiMeter
              size="sm"
              value={86.5}
              max={86.5}
              tone="success"
              label="Collected"
              valueText="Fully collected, $86.50 of $86.50"
            />$86.50</UiCluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >INV-1044</th>
        <td
          data-ui-align="end"
        >
          <UiCluster
            gap="s"
            justify="end"
            wrap="nowrap"
          ><UiMeter
              size="sm"
              value={3000}
              max={12000}
              tone="warning"
              label="Collected"
              valueText="Overdue, $3,000 of $12,000 collected"
            />$3,000.00</UiCluster>
        </td>
      </tr>
    </tbody>
  </table>
</UiTable>
import { Component } from "@angular/core";
import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable, UiCluster, UiMeter],
  template: `
    <div
      ui-table
    >
      <table>
        <caption>Invoices</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Invoice</th>
            <th
              scope="col"
              data-ui-align="end"
            >Collected</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >INV-1041</th>
            <td
              data-ui-align="end"
            >
              <div
                ui-cluster
                [gap]="'s'"
                [justify]="'end'"
                [wrap]="'nowrap'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [value]="0"
                  [max]="1240"
                  [label]="'Collected'"
                  [valueText]="'Nothing collected yet, $0 of $1,240'"
                ></span>$0.00</div>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >INV-1042</th>
            <td
              data-ui-align="end"
            >
              <div
                ui-cluster
                [gap]="'s'"
                [justify]="'end'"
                [wrap]="'nowrap'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [value]="750"
                  [max]="1240"
                  [tone]="'info'"
                  [label]="'Collected'"
                  [valueText]="'Partly collected, $750 of $1,240'"
                ></span>$750.00</div>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >INV-1043</th>
            <td
              data-ui-align="end"
            >
              <div
                ui-cluster
                [gap]="'s'"
                [justify]="'end'"
                [wrap]="'nowrap'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [value]="86.5"
                  [max]="86.5"
                  [tone]="'success'"
                  [label]="'Collected'"
                  [valueText]="'Fully collected, $86.50 of $86.50'"
                ></span>$86.50</div>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >INV-1044</th>
            <td
              data-ui-align="end"
            >
              <div
                ui-cluster
                [gap]="'s'"
                [justify]="'end'"
                [wrap]="'nowrap'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [value]="3000"
                  [max]="12000"
                  [tone]="'warning'"
                  [label]="'Collected'"
                  [valueText]="'Overdue, $3,000 of $12,000 collected'"
                ></span>$3,000.00</div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  `,
})
export default class Example {}

Checklist progress

Standing on its own, a meter takes its name from visible text through aria-labelledby, and max is the number of steps.

Setup checklist

3 of 5 steps done

Code

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

<ui-stack
  gap="xs"
>
  <ui-text
    id="setup-steps"
    size="sm"
  >Setup checklist</ui-text>
  <ui-meter
    value="3"
    max="5"
    tone="accent"
    aria-labelledby="setup-steps"
    value-text="3 of 5 steps done"
  ></ui-meter>
  <ui-text
    tone="muted"
    size="xs"
  >3 of 5 steps done</ui-text>
</ui-stack>
<script setup>
  import { UiMeter, UiStack, UiText } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="xs"
  >
    <UiText
      id="setup-steps"
      size="sm"
    >Setup checklist</UiText>
    <UiMeter
      :value="3"
      :max="5"
      tone="accent"
      aria-labelledby="setup-steps"
      value-text="3 of 5 steps done"
    />
    <UiText
      tone="muted"
      size="xs"
    >3 of 5 steps done</UiText>
  </UiStack>
</template>
import { UiMeter, UiStack, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="xs"
  >
    <UiText
      id="setup-steps"
      size="sm"
    >Setup checklist</UiText>
    <UiMeter
      value={3}
      max={5}
      tone="accent"
      aria-labelledby="setup-steps"
      valueText="3 of 5 steps done"
    />
    <UiText
      tone="muted"
      size="xs"
    >3 of 5 steps done</UiText>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiMeter, UiStack, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="xs"
  >
    <UiText
      id="setup-steps"
      size="sm"
    >Setup checklist</UiText>
    <UiMeter
      value={3}
      max={5}
      tone="accent"
      aria-labelledby="setup-steps"
      valueText="3 of 5 steps done"
    />
    <UiText
      tone="muted"
      size="xs"
    >3 of 5 steps done</UiText>
  </UiStack>;
}
<script>
  import { UiMeter, UiStack, UiText } from "@threadlabs/looma";
</script>

<UiStack
  gap="xs"
>
  <UiText
    id="setup-steps"
    size="sm"
  >Setup checklist</UiText>
  <UiMeter
    value={3}
    max={5}
    tone="accent"
    aria-labelledby="setup-steps"
    valueText="3 of 5 steps done"
  />
  <UiText
    tone="muted"
    size="xs"
  >3 of 5 steps done</UiText>
</UiStack>
import { Component } from "@angular/core";
import { UiMeter, UiStack, UiText } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiText, UiMeter],
  template: `
    <div
      ui-stack
      [gap]="'xs'"
    >
      <ng-template
        ui-text
        [size]="'sm'"
        [hnAttributes]="{ id: 'setup-steps' }"
      >Setup checklist</ng-template>
      <span
        ui-meter
        [value]="3"
        [max]="5"
        [tone]="'accent'"
        aria-labelledby="setup-steps"
        [valueText]="'3 of 5 steps done'"
      ></span>
      <ng-template
        ui-text
        [tone]="'muted'"
        [size]="'xs'"
      >3 of 5 steps done</ng-template>
    </div>
  `,
})
export default class Example {}

Steps

segments draws the bar as steps, so it reads as how far along a sequence is rather than as a percentage. max is the number of steps and value the steps done, the label beside it names the current step, and valueText says the step and where it falls.

Orders
OrderStatus
1041
<ui-meter size="sm" segments="6" value="1" max="6" label="Status" value-text="Placed, step 1 of 6"

Placed

1042
<ui-meter size="sm" segments="6" value="4" max="6" tone="info" label="Status" value-text="Shipped, step 4 of 6"

Shipped

1043
<ui-meter size="sm" segments="6" value="6" max="6" tone="success" label="Status" value-text="Delivered, step 6 of 6"

Delivered

Code

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

<ui-table>
  <table>
    <caption>Orders</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Order</th>
        <th
          scope="col"
        >Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >1041</th>
        <td>
          <ui-cluster
            gap="s"
          ><ui-meter
              size="sm"
              segments="6"
              value="1"
              max="6"
              label="Status"
              value-text="Placed, step 1 of 6"
            ></ui-meter>Placed</ui-cluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >1042</th>
        <td>
          <ui-cluster
            gap="s"
          ><ui-meter
              size="sm"
              segments="6"
              value="4"
              max="6"
              tone="info"
              label="Status"
              value-text="Shipped, step 4 of 6"
            ></ui-meter>Shipped</ui-cluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >1043</th>
        <td>
          <ui-cluster
            gap="s"
          ><ui-meter
              size="sm"
              segments="6"
              value="6"
              max="6"
              tone="success"
              label="Status"
              value-text="Delivered, step 6 of 6"
            ></ui-meter>Delivered</ui-cluster>
        </td>
      </tr>
    </tbody>
  </table>
</ui-table>
<script setup>
  import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";
</script>

<template>
  <UiTable>
    <table>
      <caption>Orders</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Order</th>
          <th
            scope="col"
          >Status</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >1041</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                :segments="6"
                :value="1"
                :max="6"
                label="Status"
                value-text="Placed, step 1 of 6"
              />Placed</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >1042</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                :segments="6"
                :value="4"
                :max="6"
                tone="info"
                label="Status"
                value-text="Shipped, step 4 of 6"
              />Shipped</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >1043</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                :segments="6"
                :value="6"
                :max="6"
                tone="success"
                label="Status"
                value-text="Delivered, step 6 of 6"
              />Delivered</UiCluster>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>
</template>
import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Orders</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Order</th>
          <th
            scope="col"
          >Status</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >1041</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                segments={6}
                value={1}
                max={6}
                label="Status"
                valueText="Placed, step 1 of 6"
              />Placed</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >1042</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                segments={6}
                value={4}
                max={6}
                tone="info"
                label="Status"
                valueText="Shipped, step 4 of 6"
              />Shipped</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >1043</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                segments={6}
                value={6}
                max={6}
                tone="success"
                label="Status"
                valueText="Delivered, step 6 of 6"
              />Delivered</UiCluster>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
/** @jsxImportSource preact */
import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";

export default function Example() {
  return <UiTable>
    <table>
      <caption>Orders</caption>
      <thead>
        <tr>
          <th
            scope="col"
          >Order</th>
          <th
            scope="col"
          >Status</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th
            scope="row"
          >1041</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                segments={6}
                value={1}
                max={6}
                label="Status"
                valueText="Placed, step 1 of 6"
              />Placed</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >1042</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                segments={6}
                value={4}
                max={6}
                tone="info"
                label="Status"
                valueText="Shipped, step 4 of 6"
              />Shipped</UiCluster>
          </td>
        </tr>
        <tr>
          <th
            scope="row"
          >1043</th>
          <td>
            <UiCluster
              gap="s"
            ><UiMeter
                size="sm"
                segments={6}
                value={6}
                max={6}
                tone="success"
                label="Status"
                valueText="Delivered, step 6 of 6"
              />Delivered</UiCluster>
          </td>
        </tr>
      </tbody>
    </table>
  </UiTable>;
}
<script>
  import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";
</script>

<UiTable>
  <table>
    <caption>Orders</caption>
    <thead>
      <tr>
        <th
          scope="col"
        >Order</th>
        <th
          scope="col"
        >Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th
          scope="row"
        >1041</th>
        <td>
          <UiCluster
            gap="s"
          ><UiMeter
              size="sm"
              segments={6}
              value={1}
              max={6}
              label="Status"
              valueText="Placed, step 1 of 6"
            />Placed</UiCluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >1042</th>
        <td>
          <UiCluster
            gap="s"
          ><UiMeter
              size="sm"
              segments={6}
              value={4}
              max={6}
              tone="info"
              label="Status"
              valueText="Shipped, step 4 of 6"
            />Shipped</UiCluster>
        </td>
      </tr>
      <tr>
        <th
          scope="row"
        >1043</th>
        <td>
          <UiCluster
            gap="s"
          ><UiMeter
              size="sm"
              segments={6}
              value={6}
              max={6}
              tone="success"
              label="Status"
              valueText="Delivered, step 6 of 6"
            />Delivered</UiCluster>
        </td>
      </tr>
    </tbody>
  </table>
</UiTable>
import { Component } from "@angular/core";
import { UiCluster, UiMeter, UiTable } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiTable, UiCluster, UiMeter],
  template: `
    <div
      ui-table
    >
      <table>
        <caption>Orders</caption>
        <thead>
          <tr>
            <th
              scope="col"
            >Order</th>
            <th
              scope="col"
            >Status</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th
              scope="row"
            >1041</th>
            <td>
              <div
                ui-cluster
                [gap]="'s'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [segments]="6"
                  [value]="1"
                  [max]="6"
                  [label]="'Status'"
                  [valueText]="'Placed, step 1 of 6'"
                ></span>Placed</div>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >1042</th>
            <td>
              <div
                ui-cluster
                [gap]="'s'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [segments]="6"
                  [value]="4"
                  [max]="6"
                  [tone]="'info'"
                  [label]="'Status'"
                  [valueText]="'Shipped, step 4 of 6'"
                ></span>Shipped</div>
            </td>
          </tr>
          <tr>
            <th
              scope="row"
            >1043</th>
            <td>
              <div
                ui-cluster
                [gap]="'s'"
              ><span
                  ui-meter
                  [size]="'sm'"
                  [segments]="6"
                  [value]="6"
                  [max]="6"
                  [tone]="'success'"
                  [label]="'Status'"
                  [valueText]="'Delivered, step 6 of 6'"
                ></span>Delivered</div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  `,
})
export default class Example {}

Distribution

Mutually exclusive categories share one bar in proportion to their counts. A complete description and a visible text legend carry meaning without colour.

Waiting 2 · Accepted 5 · Declined 2

Code

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

<ui-stack
  gap="s"
>
  <ui-meter
    variant="distribution"
    label="Requests by outcome"
    value-text="2 waiting, 5 accepted, 2 declined"
    parts="[{&quot;value&quot;:2,&quot;color&quot;:&quot;#789168&quot;},{&quot;value&quot;:5,&quot;color&quot;:&quot;#b97731&quot;},{&quot;value&quot;:2,&quot;color&quot;:&quot;#533b29&quot;}]"
  ></ui-meter>
  <ui-text
    size="sm"
  >Waiting 2 · Accepted 5 · Declined 2</ui-text>
</ui-stack>
<script setup>
  import { UiMeter, UiStack, UiText } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="s"
  >
    <UiMeter
      variant="distribution"
      label="Requests by outcome"
      value-text="2 waiting, 5 accepted, 2 declined"
      :parts="[{ value: 2, color: '#789168' }, { value: 5, color: '#b97731' }, { value: 2, color: '#533b29' }]"
    />
    <UiText
      size="sm"
    >Waiting 2 · Accepted 5 · Declined 2</UiText>
  </UiStack>
</template>
import { UiMeter, UiStack, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiMeter
      variant="distribution"
      label="Requests by outcome"
      valueText="2 waiting, 5 accepted, 2 declined"
      parts={[{"value":2,"color":"#789168"},{"value":5,"color":"#b97731"},{"value":2,"color":"#533b29"}]}
    />
    <UiText
      size="sm"
    >Waiting 2 · Accepted 5 · Declined 2</UiText>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiMeter, UiStack, UiText } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiMeter
      variant="distribution"
      label="Requests by outcome"
      valueText="2 waiting, 5 accepted, 2 declined"
      parts={[{"value":2,"color":"#789168"},{"value":5,"color":"#b97731"},{"value":2,"color":"#533b29"}]}
    />
    <UiText
      size="sm"
    >Waiting 2 · Accepted 5 · Declined 2</UiText>
  </UiStack>;
}
<script>
  import { UiMeter, UiStack, UiText } from "@threadlabs/looma";
</script>

<UiStack
  gap="s"
>
  <UiMeter
    variant="distribution"
    label="Requests by outcome"
    valueText="2 waiting, 5 accepted, 2 declined"
    parts={[{"value":2,"color":"#789168"},{"value":5,"color":"#b97731"},{"value":2,"color":"#533b29"}]}
  />
  <UiText
    size="sm"
  >Waiting 2 · Accepted 5 · Declined 2</UiText>
</UiStack>
import { Component } from "@angular/core";
import { UiMeter, UiStack, UiText } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiMeter, UiText],
  template: `
    <div
      ui-stack
      [gap]="'s'"
    >
      <span
        ui-meter
        [variant]="'distribution'"
        [label]="'Requests by outcome'"
        [valueText]="'2 waiting, 5 accepted, 2 declined'"
        [parts]="[{ value: 2, color: '#789168' }, { value: 5, color: '#b97731' }, { value: 2, color: '#533b29' }]"
      ></span>
      <ng-template
        ui-text
        [size]="'sm'"
      >Waiting 2 · Accepted 5 · Declined 2</ng-template>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: span

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
labelWhat the meter measures, as its accessible name ("Collected"). Leave it empty only when aria-labelledby names the meter from visible text.labelstring—
maxThe value of a full bar, as on a native meter: 1 for a fraction, or the whole amount (a total, a count of items) with value as the part of it. Must be above 0; otherwise the bar is empty.maxnumber1—
partsCategories in display order for distribution. Positive finite values determine each share without rounding; zero and negative values draw nothing. Supply label and valueText to describe every category, and a visible text legend so colour is never the only explanation.partslist(object)
  • [].value: number (required)
  • [].color: string (required)
[]—
segmentsHow many equal segments to draw the bar as, from 2 to 12, for a count of steps: set max to the same number and value to the steps done, so whole segments fill, and say the step in valueText ("Shipped, step 4 of 6"). A value between steps fills part of a segment. 0 draws one continuous bar, as does any other number.segmentsnumber0—
sizeThickness. sm is a thin bar that sits in a line of text, such as a table cell beside an amount; md stands on its own.sizekeywordmdsm, md
toneWhich colour the filled part speaks in, as on Badge: neutral is a quiet grey, accent draws the eye, and info, success, warning, and danger mark a state (such as nothing yet, partial, complete, or needs attention). Colour alone says nothing to assistive technology or in forced colours, so say the state in valueText or in text beside the meter too. Supporting brand families use accent-secondary and accent-tertiary; their usable colors derive from the theme, and unset families follow the primary accent.tonekeywordneutralneutral, accent, accent-secondary, accent-tertiary, info, success, warning, danger
valueHow far along, from 0 to max. A value outside that range draws at the nearer end: below 0 is an empty track, above max a full bar.valuenumber0—
value-textThe value as assistive technology reads it ("$750 of $1,240 collected", "3 of 5 done, overdue"). Empty reads the whole percentage in the current locale.valueTextstring—
variantprogress measures one value against max. distribution shows mutually exclusive parts of a whole as a full-width proportional bar; it uses parts rather than value/max and is an image, not a progress reading.variantkeywordprogressprogress, distribution

Framework props

NameTypeDefaultAllowed values
labelstring—
maxnumber1—
partsreadonly ({ readonly value: number; readonly color: string })[]
  • [].value: number (required)
  • [].color: string (required)
[]—
segmentsnumber0—
size"sm" | "md"mdsm, md
tone"neutral" | "accent" | "accent-secondary" | "accent-tertiary" | "info" | "success" | "warning" | "danger"neutralneutral, accent, accent-secondary, accent-tertiary, info, success, warning, danger
valuenumber0—
valueTextstring—
variant"progress" | "distribution"progressprogress, distribution

Events

No events.

Slots

No slots.

Design tokens

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

Component tokens

TokenDeclared valuesFallback
--ui-meter-inline-size—8rem | 5rem

Shared tokens

TokenFallback
--ui-accent-secondary-subtle-text—
--ui-accent-subtle-text—
--ui-accent-tertiary-subtle-text—
--ui-border—
--ui-border-width—
--ui-danger-subtle-text—
--ui-info-subtle-text—
--ui-motion-feedback—
--ui-motion-feedback-ease—
--ui-motion-layout—
--ui-motion-layout-ease—
--ui-radius-round—
--ui-space-0-5—
--ui-space-1—
--ui-space-2—
--ui-space-4—
--ui-success-subtle-text—
--ui-text-muted—
--ui-warning-subtle-text—