sulaf
DocsComponentsComposables
X
Sections
  • Get Started
  • Installation
  • Components
  • Composables
  • Animations
    Soon
Components
  • Autocomplete
    New
  • Show More
    New
  • Meter
    New
  • Contribution Heatmap
    New
  • Phone Input
    New
  • Typography
    New
  • Code Block
    Soon
  • Code Snippet
    Soon
  • Guided Tour
    Soon
  • Star Rating
    Soon
Composables
  • useGithubProfile
    Beta
  • useIsMac
    New
Animations
Soon

Meter

PreviousNext

A component that displays a value within a known range, like a gauge or progress indicator.

75%
<script setup lang="ts">
import { ref } from 'vue'
import {
  Meter,
  MeterHeader,
  MeterLabel,
  MeterTrack,
  MeterIndicator,
  MeterValue,
} from '@/components/ui/meter'
const value = ref(75)
</script>

<template>
  <div class="flex gap-8 max-w-64 size-full min-h-64 items-center">
    <div class="size-full flex-1">
      <Meter :value="value" orientation="horizontal" size="lg">
        <MeterHeader>
          <MeterLabel>Completion</MeterLabel>
          <MeterValue />
        </MeterHeader>
        <MeterTrack>
          <MeterIndicator />
        </MeterTrack>
      </Meter>
    </div>
  </div>
</template>

The Meter component displays a value within a known range using a visual track and indicator. It supports multiple color variants and animated transitions.

Installation

pnpm dlx sulaf@latest add meter

Usage

Basic Usage

A meter requires a root Meter component with a value, a MeterTrack containing a MeterIndicator, and an optional MeterHeader with label and value display.

<script setup lang="ts">
import { ref } from 'vue'
import {
  Meter,
  MeterHeader,
  MeterLabel,
  MeterTrack,
  MeterIndicator,
  MeterValue,
} from '@/components/ui/meter'

const value = ref(75)
</script>

<template>
  <Meter :value="value">
    <MeterHeader>
      <MeterLabel>Completion</MeterLabel>
      <MeterValue />
    </MeterHeader>
    <MeterTrack>
      <MeterIndicator />
    </MeterTrack>
  </Meter>
</template>

Examples

Variants and Sizes

Demonstrates the default, success, warning, danger variants, and sm, default, lg sizes.

Horizontal Meters

75%
75%
75%
75%

Vertical Meters

75%
75%
75%
75%

Different Sizes (Horizontal)

75%
75%
75%
<script setup lang="ts">
import { ref } from 'vue'
import {
  Meter,
  MeterHeader,
  MeterLabel,
  MeterTrack,
  MeterIndicator,
  MeterValue,
} from '@/components/ui/meter'

const value = ref(75)
</script>

<template>
  <div class="space-y-8">
    <!-- Horizontal Meters -->
    <div class="flex flex-col gap-4">
      <h3 class="text-lg font-semibold">Horizontal Meters</h3>
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <Meter :value="value" variant="default">
          <MeterHeader>
            <MeterLabel>Default</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>

        <Meter :value="value" variant="success">
          <MeterHeader>
            <MeterLabel>Success</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>

        <Meter :value="value" variant="warning">
          <MeterHeader>
            <MeterLabel>Warning</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>

        <Meter :value="value" variant="danger">
          <MeterHeader>
            <MeterLabel>Danger</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>
      </div>
    </div>

    <!-- Vertical Meters -->
    <div class="flex flex-col gap-4">
      <h3 class="text-lg font-semibold">Vertical Meters</h3>
      <div class="flex gap-4 h-64">
        <Meter :value="value" orientation="vertical" variant="default">
          <MeterTrack><MeterIndicator /></MeterTrack>
          <MeterHeader>
            <MeterLabel>Default</MeterLabel>
            <MeterValue />
          </MeterHeader>
        </Meter>

        <Meter :value="value" orientation="vertical" variant="success">
          <MeterTrack><MeterIndicator /></MeterTrack>
          <MeterHeader>
            <MeterLabel>Success</MeterLabel>
            <MeterValue />
          </MeterHeader>
        </Meter>

        <Meter :value="value" orientation="vertical" variant="warning">
          <MeterTrack><MeterIndicator /></MeterTrack>
          <MeterHeader>
            <MeterLabel>Warning</MeterLabel>
            <MeterValue />
          </MeterHeader>
        </Meter>

        <Meter :value="value" orientation="vertical" variant="danger">
          <MeterTrack><MeterIndicator /></MeterTrack>
          <MeterHeader>
            <MeterLabel>Danger</MeterLabel>
            <MeterValue />
          </MeterHeader>
        </Meter>
      </div>
    </div>

    <!-- Different Sizes -->
    <div class="flex flex-col gap-4">
      <h3 class="text-lg font-semibold">Different Sizes (Horizontal)</h3>
      <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
        <Meter :value="value" size="sm">
          <MeterHeader>
            <MeterLabel>Small</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>

        <Meter :value="value" size="default">
          <MeterHeader>
            <MeterLabel>Default</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>

        <Meter :value="value" size="lg">
          <MeterHeader>
            <MeterLabel>Large</MeterLabel>
            <MeterValue />
          </MeterHeader>
          <MeterTrack><MeterIndicator /></MeterTrack>
        </Meter>
      </div>
    </div>
  </div>
</template>

Dynamic Meter

Showcases a meter with a dynamically changing value and variant, useful for progress displays.

0%

This meter updates its value and variant dynamically to reflect progress.

<script setup lang="ts">
import { ref, onMounted, shallowRef, onUnmounted } from 'vue'
import {
  Meter,
  MeterHeader,
  MeterLabel,
  MeterTrack,
  MeterIndicator,
  MeterValue,
} from '@/components/ui/meter'
import { Button } from '@/components/ui/button'

const dynamicValue = ref(0)
const interval = shallowRef<ReturnType<typeof setInterval> | null>(null)

const startProgress = () => {
  if (interval.value) clearInterval(interval.value)
  dynamicValue.value = 0
  interval.value = setInterval(() => {
    if (dynamicValue.value < 100) {
      dynamicValue.value += 10
    } else {
      if (interval.value) clearInterval(interval.value)
      interval.value = null
    }
  }, 500)
}

onMounted(() => {
  startProgress()
})

onUnmounted(() => {
  if (interval.value) clearInterval(interval.value)
})

const getMeterVariant = (value: number) => {
  if (value < 40) return 'danger'
  if (value < 70) return 'warning'
  if (value < 100) return 'default'
  return 'success'
}
</script>

<template>
  <div class="w-full max-w-lg space-y-6">
    <Meter :value="dynamicValue" :variant="getMeterVariant(dynamicValue)">
      <MeterHeader>
        <MeterLabel>Dynamic Progress</MeterLabel>
        <MeterValue />
      </MeterHeader>
      <MeterTrack>
        <MeterIndicator />
      </MeterTrack>
    </Meter>

    <Button @click="startProgress" :disabled="dynamicValue < 100 && interval !== null">
      +
      {{ dynamicValue < 100 && interval !== null ? 'In Progress...' : 'Restart Progress' }}
    </Button>

    <p class="text-sm text-muted-foreground">
      This meter updates its value and variant dynamically to reflect progress.
    </p>
  </div>
</template>

Custom Styling

You can combine data slots, ARIA attributes, and state-based data attributes to create highly specific styles:

/* Style the indicator based on the root's variant and value */
[data-slot="meter"][data-variant="danger"][aria-valuenow="100"] [data-slot="meter-indicator"] {
  background-color: #ef4444;
  box-shadow: 0 0 10px #ef4444;
}

/* Change the label color when the meter reaches its maximum value */
[data-slot="meter"][data-percentage="100%"] [data-slot="meter-label"] {
  color: #10b981;
}

/* Target the value text when it matches a specific ARIA text representation */
[data-slot="meter"][aria-valuetext="50%"] [data-slot="meter-value"] {
  font-weight: 700;
  text-decoration: underline;
}

Accessibility

The Meter component is designed with accessibility in mind, adhering to WAI-ARIA guidelines for progress indicators. It uses the meter role and relevant ARIA attributes to convey its state to assistive technologies.

ARIA Attributes

AttributeDescription
roleThe Meter root component uses role="meter".
aria-valuenowRepresents the current value of the meter (from value prop).
aria-valueminDefines the minimum allowed value (from min prop).
aria-valuemaxDefines the maximum allowed value (from max prop).
aria-valuetextProvides a human-readable text alternative for the current value. By default, this is set to the percentage computed property (e.g., "50%"). For more context-specific information (e.g., "50 percent used", "12 GB remaining"), consider overriding this by providing a descriptive label prop to the Meter component.

| aria-label | An optional accessible label for the meter (from label prop). |

Data Attributes

The component also exposes several data attributes for styling and testing purposes:

AttributeDescription
data-variantReflects the variant prop for style targeting.
data-sizeReflects the size prop for size-specific styles.
data-valueCurrent numeric value (from value prop).
data-maxMaximum allowed value (from max prop).
data-minMinimum allowed value (from min prop).
data-percentageComputed percentage value (0-100).

Data Slots

Use these slots to target specific parts of the component for styling:

AttributeDescription
data-slot="meter"The root Meter component.
data-slot="meter-header"Container for the label and value.
data-slot="meter-label"The label component.
data-slot="meter-value"The value display component.
data-slot="meter-track"The background track component.
data-slot="meter-indicator"The visual indicator component.
Show MoreContribution Heatmap

On This Page

InstallationUsageBasic UsageExamplesVariants and SizesDynamic MeterCustom StylingAccessibilityARIA AttributesData AttributesData Slots
© 2026 - Built with shadcn-vue . The source code is available on GitHub.