Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsEssentials

Meter

Gauge displaying a known value within a fixed range — battery, disk usage, score, capacity. Linear or circular, four sizes, full intent palette with optional threshold-based auto-coloring, locale-aware formatting, and a multi-segment MeterGroup variant for stacked usage.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { Meter, MeterGroup } from "@tessinaui/ui";
{/* Basic */}
<Meter value={68} label="Storage" />

{/* Circular */}
<Meter type="circular" value={72} label="CPU" valuePosition="inside" />

{/* Threshold-based auto colour */}
<Meter
  value={92}
  label="Disk usage"
  thresholds={[
    { at: 50, intent: "primary" },
    { at: 80, intent: "warning" },
    { at: 101, intent: "error" },
  ]}
/>

{/* Locale-aware formatting */}
<Meter
  value={186.4}
  max={500}
  label="Disk"
  format={{ style: "unit", unit: "gigabyte", maximumFractionDigits: 1 }}
/>

{/* Custom range — rating */}
<Meter value={4.3} max={5} label="Rating" intent="success" showFraction />

{/* Multi-segment */}
<MeterGroup
  max={500}
  showTotal
  format={{ style: "unit", unit: "gigabyte", maximumFractionDigits: 1 }}
  segments={[
    { label: "Photos", value: 124,  intent: "primary" },
    { label: "Videos", value: 86.5, intent: "error"   },
    { label: "Music",  value: 42.3, intent: "info"    },
    { label: "Other",  value: 31.2, intent: "neutral" },
  ]}
/>

Showcase

Meter vs Progress

The two components look similar but communicate different things:

MeterProgress
PurposeA known value within a fixed rangeTask completion working toward 100%
ExamplesBattery 78%, Disk 320 GB / 512 GB, Score 4.2 / 5, RAM 6.2 GB / 16 GBFile upload, multi-step form, page load
RangeCustom min / max (default 0–100)Always 0–100
IndeterminateNo — value must be knownYes — indeterminate prop
ARIA rolerole="meter"role="progressbar"

Use Meter when the value already exists and isn't going to change as the user waits. Use Progress when something is loading.

API Reference — Meter

PropTypeDefaultDescription
valuenumber—The current value (clamped between min and max)
minnumber0Minimum
maxnumber100Maximum
type"linear" | "circular""linear"Bar or ring
size"sm" | "md" | "lg" | "xl""md"Size token
rounded"none" | "sm" | "md" | "lg" | "full""full"Corner radius (linear only)
intent"primary" | "error" | "warning" | "success" | "info" | "neutral""primary"Indicator colour
tone"solid" | "soft""solid"Saturation — soft uses *-light token
thresholdsArray<{ at: number; intent: MeterIntent }>—Auto-recolor based on value crossing breakpoints
labelReactNode—Visible label rendered above (linear) or below (circular) the meter
labelPosition"outside" | "inside" | "none""outside"Where the label appears
valuePosition"right" | "below" | "inside" | "none""right"Where the formatted value renders
showFractionbooleanfalseRender as ${value}/${max} instead of ${value}
formatIntl.NumberFormatOptions—Locale-aware formatting (currency, percent, units, compact)
localeIntl.LocalesArgumentruntimeBCP-47 locale tag
renderValue(formatted: string, value: number) => ReactNode—Fully custom value rendering — overrides format / showFraction
dir"ltr" | "rtl"inheritedText direction
classNamestring—Additional classes on the root

Thresholds

thresholds lets the meter recolour itself based on the current value. The lowest at whose value is greater than the current value wins. If no threshold matches, the base intent is used.

<Meter
  value={92}
  thresholds={[
    { at: 30, intent: "error" },     // 0–29  → error
    { at: 70, intent: "warning" },   // 30–69 → warning
    { at: 101, intent: "success" },  // 70–100 → success
  ]}
/>

For an inverted scale (e.g. battery — low is bad), reverse the order:

<Meter
  value={15}
  thresholds={[
    { at: 20, intent: "error" },
    { at: 50, intent: "warning" },
    { at: 101, intent: "success" },
  ]}
/>

API Reference — MeterGroup

PropTypeDefaultDescription
segmentsArray<MeterGroupSegment>—Stacked segments that fill the bar
minnumber0Minimum value of the scale
maxnumber | "sum"100Max value, or "sum" to derive from segment values
size"sm" | "md" | "lg" | "xl""md"Size token
rounded"none" | "sm" | "md" | "lg" | "full""full"Track corner radius
labelReactNode—Label rendered above the bar
legendPosition"above" | "below" | "none""below"Where to render the segment legend
showTotalbooleanfalseRender ${total} / ${max} next to the label
formatIntl.NumberFormatOptions—Format the total + each legend value
localeIntl.LocalesArgumentruntimeBCP-47 locale tag
dir"ltr" | "rtl"inheritedText direction

MeterGroupSegment

FieldTypeDescription
valuenumberSegment value (in same scale as min/max)
labelReactNodeDisplay label in the legend
intentMeterIntentColour for this segment (default "primary")
tone"solid" | "soft"Saturation (default "solid")
iconReactNodeOptional icon next to the legend label

Notes

  • Accessibility — built on @base-ui/react/meter. The container renders with role="meter" and aria-valuemin / aria-valuemax / aria-valuenow / aria-valuetext so assistive technologies announce the value correctly.
  • Locale-aware aria-valuetext — when format is provided, the value is formatted with Intl.NumberFormat for aria-valuetext so screen readers read e.g. "42 percent" rather than "0.42".
  • Inside value — only lg and xl linear sizes render an inline value (the smaller sizes don't have enough vertical room).
  • RTL — the linear track is mirrored via transform: scaleX(-1). The inside-value text is un-mirrored so it reads correctly.
  • max="sum" on MeterGroup — convenient when you want the bar to be exactly as long as the data fills it (e.g. a triage panel showing exact bug counts), rather than a fixed scale.

Progress

Communicate operation status with a linear bar or circular ring. Four variants (default/success/warning/error), three sizes, five rounding options, indeterminate animation, optional label with inside/outside positioning, and LTR/RTL support.

Rating

Captures user sentiment in four patterns — star scale, numeric NPS scale, emoji reaction picker, and binary thumbs — with full intent palette, half-star precision, RTL support, and keyboard navigation.

On this page

PlaygroundInstallationUsageShowcaseMeter vs ProgressAPI Reference — MeterThresholdsAPI Reference — MeterGroupMeterGroupSegmentNotes