COMPONENT / stat-band
Stat Band_
Signal-coloured ribbon of big numbers and mono labels, split by hard ground-deep rules that hold at any wrap. Requires @cybr/tokens.
Install
1. Map the
@cybrnamespace, once per project. Add this inside yourcomponents.json, or into its existing"registries"object. Nocomponents.jsonyet? Runnpx shadcn@latest initfirst to create one."registries": { "@cybr": "https://cybr-ui.com/r/{name}.json" }2. Add components:
$ npx shadcn@latest add @cybr/stat-band
If shadcn reports Unknown registry "@cybr", step 1 is missing from that project's components.json.
Preview
- MVP COMPONENTS
- 030
- CATEGORIES
- 06
- BORDER RADIUS
- 0PX
- BLURRED SHADOWS
- 0
registry/ui/stat-band.tsx
import type { ComponentProps, ReactNode } from "react";
import { cn } from "@/lib/utils";
export type Stat = { value: ReactNode; label: ReactNode };
// Signal-coloured band of big numbers. The 2px gaps over a ground-deep parent
// draw the rules, so they stay correct wherever the stats wrap. Each label is
// the <dt> and comes first in the DOM; flex-col-reverse puts the value on top.
export function StatBand({
stats,
className,
...props
}: Omit<ComponentProps<"dl">, "children"> & { stats: Stat[] }) {
return (
<dl
className={cn("flex flex-wrap gap-(--border-base) bg-ground-deep", className)}
{...props}
>
{stats.map((stat, i) => (
<div
key={i}
className="flex flex-1 basis-48 flex-col-reverse bg-signal px-8 py-5 text-ground-deep"
>
<dt className="mt-1.5 font-mono text-caption font-bold tracking-label">{stat.label}</dt>
<dd className="font-display text-5xl [line-height:0.9]">{stat.value}</dd>
</div>
))}
</dl>
);
}
Repo source. The shadcn CLI rewrites import aliases to match your project.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| stats | { value: ReactNode; label: ReactNode }[] | — | One entry per stat, in reading order. Values can be any node, such as a TelemetryCounter. |
| className | string | — | Extra classes, merged after the defaults with cn(). |
| ...props | Omit<ComponentProps<"dl">, "children"> | — | Every other native <dl> attribute is forwarded. |
Usage
Needs the stylesheet from @cybr/tokens. After the install, import it in app/globals.css:
@import "tailwindcss";
@import "../styles/cybr-tokens.css";
import { StatBand } from "@/components/ui/stat-band";
import { TelemetryCounter } from "@/components/ui/telemetry-counter";
export function Stats() {
return (
<StatBand
stats={[
{ value: "030", label: "MVP COMPONENTS" },
{ value: <TelemetryCounter value={6} minDigits={2} />, label: "CATEGORIES" },
]}
/>
);
}
Anatomy
- Band: a
--ground-deepparent with 2px gaps over--signalstats. The gaps draw the rules, so they stay correct at any count and any wrap point. Four stats at full width match the artboard. - Stat: 20px/32px padding, a 48px display value with 0.9 leading, and an 11px bold mono label 6px below it.
- Wrapping: each stat has a 12rem minimum basis and grows to fill its row.
Accessibility
- Renders a
<dl>. Each label is the<dt>and comes first in the DOM, so screen readers read "MVP COMPONENTS, 030". The value is drawn above the label withflex-direction: column-reverseonly. - Text is
--ground-deepon--signal, the overview's rule for text on the accent. If you override colours withclassName, keep text contrast at 4.5:1 or above. - An animated value, such as a TelemetryCounter, keeps its own reduced-motion and screen-reader behaviour.