Gauge

An open arc that fills to value out of max, with the reading in its center. When the value changes, the arc sweeps and the number counts to it together.

<script lang="ts">
    import * as Gauge from '@sivir-ui/svelte/components/gauge';
</script>

<Gauge.Root value={58} label="Context used" />

Installation

Copy the command below and run it in your terminal.

bunx @sivir-ui/svelte add gauge

Usage

value is required; max defaults to 100, size to md, and tone to primary. Without label, screen readers announce the meter as "72 of 100", so pass a label that says what it measures.

Gauge.Root on its own renders Track, Indicator, and Value. Compose the parts yourself to restyle or leave one out. Gauge.Value shows the whole percent by default; its children snippet receives the animated value, max, and percent. The percent reads 0 or 100 only at the bounds.

import * as Gauge from '@sivir-ui/svelte/components/gauge';

<Gauge.Root value={72} label="Monthly API usage" tone="warning">
  <Gauge.Track />
  <Gauge.Indicator />
  <Gauge.Value>
    {#snippet children({ percent })}
      {percent}%
    {/snippet}
  </Gauge.Value>
</Gauge.Root>

Examples

Live updates

Changes sweep over --motion-duration-gauge (480ms by default) and snap when reduced motion is on. Switch tone at your own thresholds to warn as the limit gets close.

Context window 46,000 of 200,000 tokens
<script lang="ts">
    import { Button } from '@sivir-ui/svelte/components/button';
    import * as Gauge from '@sivir-ui/svelte/components/gauge';

    const LIMIT = 200_000;
    const STEP = 34_000;

    let used = $state(46_000);

    const tone = $derived.by<Gauge.GaugeTone>(() => {
        const ratio = used / LIMIT;

        if (ratio >= 0.95) {
            return 'error';
        }

        if (ratio >= 0.8) {
            return 'warning';
        }

        return 'primary';
    });

    function send() {
        used = Math.min(used + STEP, LIMIT);
    }

    function reset() {
        used = 46_000;
    }
</script>

<div class="flex flex-col items-center gap-5">
    <div class="flex items-center gap-4">
        <Gauge.Root value={used} max={LIMIT} {tone} size="lg" label="Context used">
            <Gauge.Track />
            <Gauge.Indicator />
            <Gauge.Value>
                {#snippet children({ percent })}
                    {percent}%
                {/snippet}
            </Gauge.Value>
        </Gauge.Root>
        <div class="flex flex-col gap-0.5 text-sm">
            <span class="font-medium text-foreground">Context window</span>
            <span class="text-foreground-muted tabular-nums">
                {used.toLocaleString('en-US')}
                of {LIMIT.toLocaleString('en-US')} tokens
            </span>
        </div>
    </div>
    <div class="flex gap-2">
        <Button size="sm" onclick={send} disabled={used >= LIMIT}>Send message</Button>
        <Button size="sm" variant="outline" onclick={reset}>Reset</Button>
    </div>
</div>

Sizes

sm is 20px, md is 32px, and lg is 56px. Root leaves Value out at sm, where the number would be too small to read.

<script lang="ts">
    import * as Gauge from '@sivir-ui/svelte/components/gauge';
</script>

<div class="flex items-center justify-center gap-6">
    <Gauge.Root value={64} size="sm" label="Small gauge" />
    <Gauge.Root value={64} size="md" label="Medium gauge" />
    <Gauge.Root value={64} size="lg" label="Large gauge" />
</div>

Context window

At sm, put the reading in text beside the gauge.

38% of context used
<script lang="ts">
    import * as Gauge from '@sivir-ui/svelte/components/gauge';
</script>

<div class="flex items-center gap-2 text-sm text-foreground-muted">
    <Gauge.Root value={38} size="sm" label="Context used" />
    <span class="tabular-nums">38% of context used</span>
</div>

Usage limit

Set max to the limit and format the center with the Value snippet.

Monthly API usage 72,000 of 100,000 requests
<script lang="ts">
    import * as Gauge from '@sivir-ui/svelte/components/gauge';
</script>

<div class="flex items-center gap-4">
    <Gauge.Root value={72_000} max={100_000} label="Monthly API usage" tone="warning" size="lg">
        <Gauge.Track />
        <Gauge.Indicator />
        <Gauge.Value>
            {#snippet children({ percent })}
                {percent}%
            {/snippet}
        </Gauge.Value>
    </Gauge.Root>
    <div class="flex flex-col gap-0.5 text-sm">
        <span class="font-medium text-foreground">Monthly API usage</span>
        <span class="text-foreground-muted tabular-nums">72,000 of 100,000 requests</span>
    </div>
</div>

Anatomy

Gauge.Root - Sets the value, max, tone, and size of the meter.

Gauge.Track - Draws the full arc behind the reading.

Gauge.Indicator - Fills the arc up to the value.

Gauge.Value - Shows the reading in the center of the arc.