Componentfeedback1.11 KB gzipZero dependencies

Meter

Turnstile capacity meter with notched frame, optimal indicator and tick marks.

Preview

Live Preview
TURNSTILE CAPACITY68%
0255075100
ZONE RATINGOPTIMAL

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add meter

Or install the full npm package:

npm install bigbullui
import { Meter } from "@/components/ui/meter";

<Meter value={68} min={0} max={100} label="TURNSTILE CAPACITY" />

Usage guidance

Use Meter when a ticket stub surface needs Turnstile capacity meter with notched frame, optimal indicator and tick marks. Keep props minimal and prefer composition with sibling stubs.

Avoid Meter for long form flows or dense data grids where a dedicated layout block fits better.

Source code

meter.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface MeterProps extends React.HTMLAttributes<HTMLDivElement> {
  value: number; // 0 to 100
  min?: number;
  max?: number;
  label?: string;
  unit?: string;
  low?: number;
  high?: number;
  optimum?: number;
  notched?: boolean;
  showTicks?: boolean;
  className?: string;
}

export function Meter({
  value,
  min = 0,
  max = 100,
  label = "TURNSTILE OCCUPANCY",
  unit = "%",
  low = 30,
  high = 80,
  optimum = 50,
  notched = true,
  showTicks = true,
  className,
  ...props
}: MeterProps) {
  const clampedValue = Math.max(min, Math.min(max, value));
  const percent = ((clampedValue - min) / (max - min)) * 100;

  // Determine status tone
  let tone = "bg-primary";
  let statusText = "OPTIMAL";

  if (clampedValue > high) {
    tone = "bg-destructive";
    statusText = "HIGH CAPACITY";
  } else if (clampedValue < low) {
    tone = "bg-accent";
    statusText = "LOW LOAD";
  }

  return (
    <div
      className={cn(
        "relative flex flex-col rounded-lg border-2 border-foreground bg-card p-4 font-mono select-none shadow-sm",
        className
      )}
      {...props}
    >
      {notched && (
        <>
          <div
            aria-hidden="true"
            className="absolute -left-1.5 top-1/2 -translate-y-1/2 size-3 rounded-full border border-foreground bg-background"
          />
          <div
            aria-hidden="true"
            className="absolute -right-1.5 top-1/2 -translate-y-1/2 size-3 rounded-full border border-foreground bg-background"
          />
        </>
      )}

      {/* Header */}
      <div className="flex items-center justify-between border-b border-dashed border-border pb-2 text-[10px] tracking-widest text-muted-foreground">
        <span>{label}</span>
        <span className="font-bold text-foreground">
          {clampedValue}
          {unit}
        </span>
      </div>

      {/* Meter Track */}
      <div className="relative mt-3 h-6 w-full rounded border-2 border-foreground bg-secondary/60 p-0.5 overflow-hidden shadow-inner">
        <div
          style={{ width: `${percent}%` }}
          className={cn(
            "h-full rounded-xs transition-all duration-300 ease-out",
            tone
          )}
        />
      </div>

      {/* Tick Marks */}
      {showTicks && (
        <div className="mt-1 flex justify-between px-0.5 text-[9px] text-muted-foreground">
          <span>{min}</span>
          <span>{Math.round((max - min) * 0.25 + min)}</span>
          <span>{Math.round((max - min) * 0.5 + min)}</span>
          <span>{Math.round((max - min) * 0.75 + min)}</span>
          <span>{max}</span>
        </div>
      )}

      {/* Status Eyebrow */}
      <div className="mt-2 flex items-center justify-between border-t border-dashed border-border pt-2 text-[9px] text-muted-foreground uppercase">
        <span>ZONE RATING</span>
        <span className="font-bold text-accent">{statusText}</span>
      </div>
    </div>
  );
}

Props

PropTypeDescription
valuenumberCurrent numeric value (0 to 100)
minnumberMinimum value (default 0)
maxnumberMaximum value (default 100)
labelstringLabel shown on top
unitstringUnit symbol (default "%")