# Gauge

Circular meter for bounded quantities such as context remaining, usage limits, storage, and seats.

- Package: `@mielui/svelte`
- Component version: `1.1.0`
- Depends on Mielui components: _internal/utils, skeleton
- Shared utilities: components/_internal/theme-changes, transition, utils.cn

## Install

~~~~sh
pnpm dlx @mielui/svelte add gauge
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Gauge

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| labels | GaugeLabels \| undefined | - | No | No | Overrides the built-in text and accessible names. Every key is optional and English is the fallback. |
| value | number \| null | - | Yes | No | The bounded quantity shown by the filled arc. Pass null when no measurement is available. |
| loading | boolean \| undefined | false | No | No | Show a loading ring while the measurement is being fetched. |
| max | number \| undefined | 100 | No | No | Value that fills the gauge. |
| label | string \| undefined | - | No | No | Describes the quantity, such as "Context remaining" or "Monthly API usage". |
| size | number \| undefined | 120 | No | No | Diameter in pixels. Defaults to 120; use an explicit size for inline meters. |
| strokeWidth | number \| undefined | - | No | No | Track and arc thickness in pixels. Defaults to 9.75% of the diameter, with a 3px minimum. |
| animation | "reveal" \| "live" \| "none" \| undefined | 'reveal' | No | No | Reveal the arc, highlight it continuously, or disable animation. |
| tone | GaugeTone \| undefined | 'primary' | No | No | Color of the fill. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Public types

~~~~ts
import type { DefaultProps } from '@mielui/svelte/utils';
import Gauge from './gauge.svelte';

export type GaugeTone = 'primary' | 'muted' | 'success' | 'warning' | 'error';

export type GaugeLabels = {
    /** Accessible name while loading. `name` is the gauge's `label`, when it has one. */
    loading?: (name: string | undefined) => string;
    /** Accessible name when there is no measurement. */
    empty?: (name: string | undefined) => string;
    /** Accessible name of a gauge with no `label`. */
    value?: (value: number, max: number) => string;
};

export type GaugeProps = {
    /**
     * Overrides the built-in text and accessible names. Every key is optional and English is the
     * fallback.
     */
    labels?: GaugeLabels;
    /** The bounded quantity shown by the filled arc. Pass null when no measurement is available. */
    value: number | null;
    /** Show a loading ring while the measurement is being fetched. */
    loading?: boolean;
    /** Value that fills the gauge. */
    max?: number;
    /** Describes the quantity, such as "Context remaining" or "Monthly API usage". */
    label?: string;
    /** Diameter in pixels. Defaults to 120; use an explicit size for inline meters. */
    size?: number;
    /** Track and arc thickness in pixels. Defaults to 9.75% of the diameter, with a 3px minimum. */
    strokeWidth?: number;
    /** Reveal the arc, highlight it continuously, or disable animation. */
    animation?: 'reveal' | 'live' | 'none';
    /** Color of the fill. */
    tone?: GaugeTone;
} & DefaultProps;

export { Gauge };
export default Gauge;
~~~~

## Examples

### Context Window

~~~~svelte
<script lang="ts">
    import { Gauge } from '@mielui/svelte/components/gauge';
</script>

<div class="flex flex-wrap items-center justify-center gap-10">
    <div class="flex items-center gap-3">
        <Gauge value={85} label="Context remaining" size={32} tone="muted">85</Gauge>
        <div class="text-sm">
            <p class="font-medium text-foreground">Context remaining</p>
            <p class="text-foreground-muted">85% available</p>
        </div>
    </div>
    <Gauge value={85} label="Context remaining" size={160}>
        <span class="flex flex-col items-center gap-2">
            <span>85<span class="text-xl text-foreground-muted">%</span></span>
            <span class="text-xs font-normal tracking-normal text-foreground-muted">remaining</span>
        </span>
    </Gauge>
</div>
~~~~

### Hero

~~~~svelte
<script lang="ts">
    import { numberShuffle } from '@mielui/svelte/actions/number-shuffle';
    import { Button } from '@mielui/svelte/components/button';
    import { Gauge } from '@mielui/svelte/components/gauge';
    import { fromAction } from 'svelte/attachments';

    let usage = $state(72);

    function updateUsage() {
        usage = usage === 72 ? 91 : 72;
    }
</script>

<div class="flex flex-col items-center gap-6">
    <Gauge value={usage} label="Monthly API usage" tone={usage > 85 ? 'warning' : 'primary'}>
        <span>
            <span {@attach fromAction(numberShuffle, () => ({ value: usage }))}>{usage}</span>
            <span class="text-base text-foreground-muted">%</span>
        </span>
    </Gauge>
    <div class="text-center">
        <p class="font-medium text-foreground">Monthly API usage</p>
        <p class="mt-1 text-sm text-foreground-muted">{usage},000 of 100,000 requests</p>
    </div>
    <Button variant="secondary" onclick={updateUsage}>Update usage</Button>
</div>
~~~~

### Interactive

~~~~svelte
<script lang="ts">
    import { numberShuffle } from '@mielui/svelte/actions/number-shuffle';
    import { Button } from '@mielui/svelte/components/button';
    import { Gauge } from '@mielui/svelte/components/gauge';
    import { fromAction } from 'svelte/attachments';

    let used = $state(48);

    function addFiles() {
        used = Math.min(64, used + 8);
    }

    function clearFiles() {
        used = 16;
    }
</script>

<div class="flex flex-col items-center gap-5">
    <Gauge
        value={used}
        max={64}
        label="Storage used in gigabytes"
        tone={used >= 56 ? 'warning' : 'primary'}
    >
        <span class="flex flex-col items-center gap-1">
            <span {@attach fromAction(numberShuffle, () => ({ value: used }))}>{used}</span>
            <span class="text-xs font-normal text-foreground-muted">of 64 GB</span>
        </span>
    </Gauge>
    <div class="flex flex-wrap gap-2">
        <Button onclick={addFiles} disabled={used === 64}>Add 8 GB</Button>
        <Button variant="secondary" onclick={clearFiles}>Clear old files</Button>
    </div>
</div>
~~~~

### States

~~~~svelte
<script lang="ts">
    import { Button } from '@mielui/svelte/components/button';
    import { Gauge } from '@mielui/svelte/components/gauge';
    import * as Tabs from '@mielui/svelte/components/tabs';

    const ANIMATIONS = ['reveal', 'live', 'none'] as const;
    const VALUES: Record<string, number | null> = {
        ready: 64,
        loading: 64,
        empty: null,
        zero: 0,
        full: 100
    };

    let dataState = $state('ready');
    let animation = $state('reveal');
    let replay = $state(0);

    const motion = $derived(
        ANIMATIONS.find((option) => {
            return option === animation;
        }) ?? 'reveal'
    );

    function replayAnimation() {
        replay += 1;
    }
</script>

<div class="w-full space-y-6">
    <div class="flex flex-wrap items-center gap-3">
        <Tabs.Root bind:value={dataState} variant="ghost">
            <div role="group" aria-label="Storage data state">
                <Tabs.List>
                    <Tabs.Trigger value="ready">Ready</Tabs.Trigger>
                    <Tabs.Trigger value="loading">Loading</Tabs.Trigger>
                    <Tabs.Trigger value="empty">No data</Tabs.Trigger>
                    <Tabs.Trigger value="zero">Zero</Tabs.Trigger>
                    <Tabs.Trigger value="full">Full</Tabs.Trigger>
                </Tabs.List>
            </div>
        </Tabs.Root>
        <Tabs.Root bind:value={animation} variant="ghost">
            <div role="group" aria-label="Gauge animation">
                <Tabs.List>
                    <Tabs.Trigger value="reveal">Reveal</Tabs.Trigger>
                    <Tabs.Trigger value="live">Live</Tabs.Trigger>
                    <Tabs.Trigger value="none">None</Tabs.Trigger>
                </Tabs.List>
            </div>
        </Tabs.Root>
        <Button variant="secondary" onclick={replayAnimation}>Replay</Button>
    </div>
    <div class="flex min-h-64 items-center justify-center">
        {#key `${animation}-${replay}`}
            <div class="flex flex-col items-center gap-3">
                <Gauge
                    value={VALUES[dataState]}
                    loading={dataState === 'loading'}
                    max={100}
                    animation={motion}
                    label="Storage used in GB"
                />
                <p class="text-sm text-foreground-muted">GB used of 100 GB</p>
            </div>
        {/key}
    </div>
</div>
~~~~

### Usage Limit

~~~~svelte
<script lang="ts">
    import { Gauge } from '@mielui/svelte/components/gauge';
</script>

<div class="flex flex-wrap justify-center gap-10">
    <div class="flex flex-col items-center gap-4">
        <Gauge value={48} max={64} label="Storage used in gigabytes" tone="primary">
            <span class="flex flex-col items-center gap-1">
                <span>48</span>
                <span class="text-xs font-normal text-foreground-muted">of 64 GB</span>
            </span>
        </Gauge>
        <p class="text-sm text-foreground-muted">Storage</p>
    </div>
    <div class="flex flex-col items-center gap-4">
        <Gauge value={18} max={20} label="Team seats used" tone="warning">18</Gauge>
        <p class="text-sm text-foreground-muted">18 of 20 seats</p>
    </div>
    <div class="flex flex-col items-center gap-4">
        <Gauge value={100} label="Backup completion" tone="success">
            100<span class="text-sm">%</span>
        </Gauge>
        <p class="text-sm text-foreground-muted">Backup complete</p>
    </div>
</div>
~~~~

For the rendered reference, visit [/docs/components/gauge](/docs/components/gauge).
