Feedback
Meter
A measurement within a known range: space used, ink left, how full a room is.
Installation
npx sinaui add meterCopy src/lib/ui/Meter.svelte, RollingNumber.svelte and tokens.css. No dependencies.
For work on its way to done (an upload, a download), use Progress instead.
Examples
Basic
Shelf space used
<script lang="ts">
import Meter from '#lib/ui/Meter.svelte';
</script>
<Meter label="Shelf space used" value={73} />Good, fair and poor ranges
Set low, high and optimum as on HTML's own meter: past 60% the bar turns amber, past 85% red.
Reading room shelves
<script lang="ts">
import Button from '#lib/ui/Button.svelte';
import Meter from '#lib/ui/Meter.svelte';
// Emptier shelves are better: under 60% is fine, over 85% needs a new room.
let used = $state(45);
</script>
<div class="stack">
<Meter label="Reading room shelves" value={used} low={60} high={85} optimum={0} />
<div class="actions">
<Button variant="secondary" size="sm" onclick={() => (used = Math.max(0, used - 15))}>
Move volumes out
</Button>
<Button variant="secondary" size="sm" onclick={() => (used = Math.min(100, used + 15))}>
Shelve a delivery
</Button>
</div>
</div>
<style>
.stack {
display: grid;
gap: 1rem;
}
.actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.5rem;
}
</style>Your own units
Here more is better, so the bar turns red when it runs low.
Ink in the copyists’ room
<script lang="ts">
import Meter from '#lib/ui/Meter.svelte';
const liters = new Intl.NumberFormat('en', {
style: 'unit',
unit: 'liter',
maximumFractionDigits: 1
});
</script>
<Meter
label="Ink in the copyists’ room"
value={3.5}
max={20}
low={5}
high={12}
optimum={20}
format={(v, _, max) => `${liters.format(v)} of ${liters.format(max)}`}
/>What it's made of
Give segments and the bar splits into parts, one shade each, with a legend. Add scans: every number rolls and the bar settles into place.
Library storage
- Manuscripts 4.2 GB
- Scans 5.1 GB
- Backups 3.1 GB
<script lang="ts">
import Button from '#lib/ui/Button.svelte';
import Meter from '#lib/ui/Meter.svelte';
let parts = $state([
{ label: 'Manuscripts', value: 4.2 },
{ label: 'Scans', value: 5.1 },
{ label: 'Backups', value: 3.1 }
]);
const used = $derived(parts.reduce((sum, p) => sum + p.value, 0));
const gb = new Intl.NumberFormat('en', {
style: 'unit',
unit: 'gigabyte',
maximumFractionDigits: 1
});
</script>
<Meter
label="Library storage"
value={used}
max={15}
high={13}
optimum={0}
segments={parts}
formatSegment={(v) => gb.format(v)}
format={(v, _, max) => `${gb.format(v)} of ${gb.format(max)}`}
/>
<Button
size="sm"
variant="secondary"
onclick={() => (parts[1].value = parts[1].value >= 7 ? 5.1 : parts[1].value + 1.2)}
>{parts[1].value >= 7 ? 'Clear old scans' : 'Add scans'}</Button
>Props
| Prop | Type | Default |
|---|---|---|
label | string (required) | |
value | number (required) | |
min, max | number | 0, 100 |
low, high, optimum | number: the ranges | |
format | (value, min, max) => string | a percentage |
segments | { label, value }[]: the parts of the value | |
formatSegment | (value) => string: each part's amount | a number |
showValue | boolean | true |
locale | string | 'en' |
Accessibility
- A meter: screen readers read its label and the formatted value (“3.5 L of 20 L”).
- Color is never the only sign: the number is always there, and turns color with the bar.
- It fills from empty when it appears; with reduced motion it simply shows.