VibeUI
ComponentsDocs
VibeUI

Components with taste, that you own. Copy the source, retint the tokens, keep every line.

Library

  • All components
  • Signature pieces
  • Text effects
  • Footers

Docs

  • Introduction
  • Installation
  • Theming
  • CLI

Project

  • GitHub
  • Agents and MCP
  • Issues
  • MIT license
© 2026 VibeUI · MIT licensed
  1. Components
  2. Feedback
  3. Meter

Meter

Segmented gauge for a value within a known range.

FeedbackServer-safe
Storage72%
Healthy38%
Almost full94%

Install

bash
npx @agents-npm-packages/vibeui add meter --dir ./src/components/ui

Import

tsx
import { Meter } from "@/components/ui/meter"

API

Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.

<Meter>

PropTypeDefaultDescription
value*number—Controlled value.
maxnumber100Upper bound.
labelstring—Visible label.
tone"sage" | "sand" | "rose" | "accent" | "ink""ink"Semantic color: neutral, sky, sage, sand, rose.
segmentsnumber20Number of segments in the gauge.
classNamestring—Extra classes, merged with the component's own.

Source

src/components/ui/meter.tsx
import { cn } from "@/lib/utils";

const tones = {
  ink: "bg-ink",
  accent: "bg-accent",
  sage: "bg-pastel-sage-ink",
  sand: "bg-pastel-sand-ink",
  rose: "bg-pastel-rose-ink",
} as const;

/** Segmented gauge for a value within a known range. */
export function Meter({
  value,
  max = 100,
  label,
  tone = "ink",
  segments = 20,
  className,
}: {
  value: number;
  max?: number;
  label?: string;
  tone?: keyof typeof tones;
  segments?: number;
  className?: string;
}) {
  const pct = Math.min(100, Math.max(0, (value / max) * 100));
  const filled = Math.round((pct / 100) * segments);

  return (
    <div className={cn("w-full", className)}>
      {label ? (
        <div className="mb-2 flex items-center justify-between text-[13px]">
          <span className="font-medium text-ink-soft">{label}</span>
          <span className="font-mono text-xs tabular-nums text-muted">
            {Math.round(pct)}%
          </span>
        </div>
      ) : null}
      <div
        role="meter"
        aria-valuenow={value}
        aria-valuemin={0}
        aria-valuemax={max}
        aria-label={label}
        className="flex h-2.5 w-full gap-[3px]"
      >
        {Array.from({ length: segments }, (_, i) => (
          <span
            key={i}
            className={cn(
              "h-full flex-1 animate-pop-in rounded-[2px] transition-colors duration-300",
              i < filled ? tones[tone] : "bg-surface-sunken",
            )}
            style={{ transitionDelay: `${i * 12}ms`, animationDelay: `${i * 22}ms` }}
          />
        ))}
      </div>
    </div>
  );
}

Exports Meter. Peer dependencies: clsx, tailwind-merge.

PreviousGaugeNextNotification