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.
| Invoice | Collected |
|---|---|
| INV-1041 |
<ui-meter
size="sm"
value="0"
max="1240"
label="Collected"
value-text="Nothing collected yet, $0 of $1,240"
|
| INV-1042 |
<ui-meter
size="sm"
value="750"
max="1240"
tone="info"
label="Collected"
value-text="Partly collected, $750 of $1,240"
|
| 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"
|
| INV-1044 |
<ui-meter
size="sm"
value="3000"
max="12000"
tone="warning"
label="Collected"
value-text="Overdue, $3,000 of $12,000 collected"
|
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.
| Order | Status |
|---|---|
| 1041 |
<ui-meter
size="sm"
segments="6"
value="1"
max="6"
label="Status"
value-text="Placed, step 1 of 6"
|
| 1042 |
<ui-meter
size="sm"
segments="6"
value="4"
max="6"
tone="info"
label="Status"
value-text="Shipped, step 4 of 6"
|
| 1043 |
<ui-meter
size="sm"
segments="6"
value="6"
max="6"
tone="success"
label="Status"
value-text="Delivered, step 6 of 6"
|
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="[{"value":2,"color":"#789168"},{"value":5,"color":"#b97731"},{"value":2,"color":"#533b29"}]"
></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
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
label | What the meter measures, as its accessible name ("Collected"). Leave it empty only when aria-labelledby names the meter from visible text. | label | string | | — |
max | The 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. | max | number | 1 | — |
parts | Categories 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. | parts | list(object)
| [] | — |
segments | How 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. | segments | number | 0 | — |
size | Thickness. 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. | size | keyword | md | sm, md |
tone | Which 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. | tone | keyword | neutral | neutral, accent, accent-secondary, accent-tertiary, info, success, warning, danger |
value | How 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. | value | number | 0 | — |
value-text | The 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. | valueText | string | | — |
variant | progress 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. | variant | keyword | progress | progress, distribution |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
label | string | | — |
max | number | 1 | — |
parts | readonly ({ readonly value: number; readonly color: string })[]
| [] | — |
segments | number | 0 | — |
size | "sm" | "md" | md | sm, md |
tone | "neutral" | "accent" | "accent-secondary" | "accent-tertiary" | "info" | "success" | "warning" | "danger" | neutral | neutral, accent, accent-secondary, accent-tertiary, info, success, warning, danger |
value | number | 0 | — |
valueText | string | | — |
variant | "progress" | "distribution" | progress | progress, 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
| Token | Declared values | Fallback |
|---|---|---|
--ui-meter-inline-size | — | 8rem | 5rem |
Shared tokens
| Token | Fallback |
|---|---|
--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 | — |