<script setup lang="ts">
import { MeterGroup, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Documents', value: 42 },
{ label: 'Photos', value: 27 },
{ label: 'Videos', value: 13 },
{ label: 'Other', value: 8 },
]
</script>
<template>
<MeterGroup label="Storage" :items="items" class="w-96" />
</template>
Usage
import { MeterGroup } from '@hina-ui/vue'
Each entry in items has a label and a value, and may set a tone; max is the total, 100 by default. The segments sit side by side on one track in proportion to their values, and the legend below lists each segment’s label and value.
<script setup lang="ts">
import { MeterGroup, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Read', value: 56 },
{ label: 'Reading', value: 19 },
]
</script>
<template>
<MeterGroup :items="items" class="w-96" />
</template>
Examples
Label and total
label sits above the track, with the sum of the segments at the end of the same line.
<script setup lang="ts">
import { MeterGroup, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Read', value: 56 },
{ label: 'Reading', value: 19 },
{ label: 'To read', value: 12 },
]
</script>
<template>
<MeterGroup label="This year's books" :items="items" class="w-96" />
</template>
Changing values
When several values change at once, each segment’s width transitions smoothly to its new share, and the legend updates with it.
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Button, MeterGroup, Stack, type MeterItem } from '@hina-ui/vue'
const labels = ['Documents', 'Photos', 'Videos', 'Other']
const sets = [
[42, 27, 13, 8],
[12, 36, 31, 6],
[25, 8, 48, 14],
]
const index = ref(0)
const items = computed<MeterItem[]>(() =>
labels.map((label, i) => ({ label, value: sets[index.value]![i]! })),
)
</script>
<template>
<Stack gap="md" align="start" class="w-96">
<MeterGroup label="Storage" :items="items" class="w-full" />
<Button size="sm" variant="soft" tone="neutral" @click="index = (index + 1) % sets.length">
Next data set
</Button>
</Stack>
</template>
Tones
Segments without a tone take the accent, info, success, warning, danger and neutral colours in that order; a tone can also be set per segment.
<script setup lang="ts">
import { MeterGroup, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Approved', value: 62, tone: 'success' },
{ label: 'Pending', value: 21, tone: 'warning' },
{ label: 'Returned', value: 9, tone: 'danger' },
]
</script>
<template>
<MeterGroup label="Review results" :items="items" class="w-96" />
</template>
Value format
format receives a segment’s value and the total and returns the text to show. The same text is what assistive technology reads as the value.
<script setup lang="ts">
import { MeterGroup, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Documents', value: 38 },
{ label: 'Photos', value: 24 },
{ label: 'Videos', value: 46 },
]
const format = (value: number) => `${value} GB`
</script>
<template>
<MeterGroup label="Storage" :items="items" :max="256" :format="format" class="w-96" />
</template>
Sizes
Three heights, the same as Progress, md by default.
<script setup lang="ts">
import { MeterGroup, Stack, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Read', value: 56 },
{ label: 'Reading', value: 19 },
]
</script>
<template>
<Stack gap="lg" class="w-96">
<MeterGroup :items="items" size="sm" :legend="false" />
<MeterGroup :items="items" :legend="false" />
<MeterGroup :items="items" size="lg" :legend="false" />
</Stack>
</template>
Without a legend
Set legend to false to keep only the track.
<script setup lang="ts">
import { MeterGroup, type MeterItem } from '@hina-ui/vue'
const items: MeterItem[] = [
{ label: 'Documents', value: 42 },
{ label: 'Photos', value: 27 },
{ label: 'Videos', value: 13 },
]
</script>
<template>
<MeterGroup label="Storage" :items="items" :legend="false" class="w-96" />
</template>
Behavior
- Segment widths transition smoothly when values change, with duration and easing from the motion variables.
- An out-of-range value is clamped: below 0 counts as 0, above
maxcounts asmax. - When the segments add up to more than
max, the excess is clipped by the track; each segment’s value and width still correspond.
Accessibility
- Each segment carries
role="meter", named by itslabel, witharia-valuenow,aria-valueminandaria-valuemaxfollowing the value andmax. - The track carries
role="group", named bylabel. - With
formatthe result is also written to each segment’saria-valuetext. - The dots in the legend are hidden from assistive technology; labels and values are plain text.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | MeterItem[] | — | Required. The segments |
max | number | 100 | Total |
label | string | — | Label above the track |
legend | boolean | true | Whether to show the legend |
format | (value: number, max: number) => string | percentage | Format of the value text |
size | 'sm' | 'md' | 'lg' | 'md' | Height |
class | string | — | Extra classes on the root element |
Other attributes fall through to the track element carrying role="group".
Types
| Field | Type | Description |
|---|---|---|
label | string | Required. Label of the segment |
value | number | Required. Value of the segment |
tone | 'accent' | 'neutral' | 'success' | 'warning' | 'danger' | 'info' | Colour of the segment, assigned in order |