Components
Meter
A bounded ring gauge for a level (battery, signal, effort) — distinct from Progress, which tracks task completion. The arc fills via an SVG stroke-dashoffset; value is clamped to [0, max].
Installation
npx @glasskit-ui/cli add meterInstall the SDK (it provides GlassViewport, useDpad and the stylesheet), then copy these files into your project:
npm install @glasskit-ui/react// components/lib/utils.tsexport type ClassValue = string | number | null | undefined | false;/** * Join truthy class names. Dependency-free on purpose: the lens components * style via bespoke semantic classes (no conflicting Tailwind utilities to * de-dupe), so this needs no clsx/tailwind-merge and resolves from anywhere * the registry is vendored. */export function cn(...inputs: ClassValue[]): string { return inputs.filter(Boolean).join(" ");}/** * Accessible name from a free-form `label` prop: the label itself when it's a * plain string, otherwise undefined (a ReactNode can't become an aria-label). */export function stringLabel(label: unknown): string | undefined { return typeof label === "string" ? label : undefined;}// components/glasskit/meter.tsximport type { ReactNode } from "react";import { cn, stringLabel } from "../lib/utils";const R = 42;const CIRC = 2 * Math.PI * R;/** * <Meter> — a bounded ring gauge for a level (battery, signal, intensity), as * opposed to <Progress> which tracks task completion. The fill is revealed with * an SVG `stroke-dashoffset` *attribute* (not inline style); `value` is clamped * to [0, max]. */export function Meter({ value, max = 100, label, unit, className,}: { value: number; max?: number; label?: ReactNode; unit?: ReactNode; className?: string;}) { const clamped = Math.max(0, Math.min(value, max)); const pct = max > 0 ? clamped / max : 0; const offset = CIRC * (1 - pct); return ( <div className={cn("gk-meter", className)} role="meter" aria-valuenow={clamped} aria-valuemin={0} aria-valuemax={max} aria-label={stringLabel(label)} > <svg viewBox="0 0 100 100" className="gk-meter__dial"> <circle cx="50" cy="50" r={R} className="gk-meter__track" /> <circle cx="50" cy="50" r={R} className="gk-meter__fill" strokeDasharray={CIRC} strokeDashoffset={offset} transform="rotate(-90 50 50)" /> </svg> <span className="gk-meter__center"> <span className="gk-meter__value t-readout"> {value} {unit != null ? <span className="gk-meter__unit">{unit}</span> : null} </span> {label != null ? ( <span className="gk-meter__label t-caption">{label}</span> ) : null} </span> </div> );}Usage
<Meter value={72} max={100} label="Effort" unit="%" />Props
Prop
Type
Heading
A screen/section title with an optional accent eyebrow above it. Pure display — one heading per view keeps the glance cheap.
Progress
Emitted progress in two shapes: a continuous linear bar (a native <progress>, so the fill needs no inline style; also covers countdowns) and discrete step-of-N dots for wizards.