ComponentsDelta Badge
Delta Badge
Up is not always good. DeltaBadge colors a change by what it means for the metric, and always shows the direction with an icon and a sign.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry filesImplemented with React and browser APIs, without an additional behavior or rendering library.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import { DeltaBadge } from "@dethink/components";Meaning across variants, and custom units.
Direction and meaning
Churn falling and latency rising both flip color when positiveDirection is down. The error-budget change sits under the neutral threshold.
| Metric | soft | outline | plain |
|---|---|---|---|
| Revenue | Up 12.4% vs last month | Up 12.4% vs last month | Up 12.4% vs last month |
| Active users | Down 3.1% vs last month | Down 3.1% vs last month | Down 3.1% vs last month |
| ChurnLower is better | Down 0.8% vs last month | Down 0.8% vs last month | Down 0.8% vs last month |
| p95 latencyLower is better | Up 6.5% vs last month | Up 6.5% vs last month | Up 6.5% vs last month |
| Error budget | No change vs last month | No change vs last month | No change vs last month |
Show sourceexamples/delta-badge/meanings.tsx
"use client";
import { DeltaBadge, type DeltaBadgeVariant } from "@dethink/components";
const variants: DeltaBadgeVariant[] = ["soft", "outline", "plain"];
const rows = [
{ metric: "Revenue", value: 12.4, positiveDirection: "up" as const },
{ metric: "Active users", value: -3.1, positiveDirection: "up" as const },
{ metric: "Churn", value: -0.8, positiveDirection: "down" as const },
{ metric: "p95 latency", value: 6.5, positiveDirection: "down" as const },
{ metric: "Error budget", value: 0.1, positiveDirection: "up" as const },
];
export function DeltaBadgeMeanings() {
return (
<div className="w-full overflow-x-auto">
<table className="w-full min-w-[28rem] text-sm">
<thead>
<tr className="text-muted-foreground text-start text-xs">
<th scope="col" className="pb-3 text-start font-medium">
Metric
</th>
{variants.map((variant) => (
<th
key={variant}
scope="col"
className="pb-3 text-start font-medium"
>
{variant}
</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row) => (
<tr key={row.metric} className="border-border border-t">
<th scope="row" className="py-3 text-start font-medium">
{row.metric}
{row.positiveDirection === "down" ? (
<span className="text-muted-foreground block text-xs font-normal">
Lower is better
</span>
) : null}
</th>
{variants.map((variant) => (
<td key={variant} className="py-3">
<DeltaBadge
value={row.value}
positiveDirection={row.positiveDirection}
neutralThreshold={0.25}
comparison="vs last month"
variant={variant}
/>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}Units
Percent by default. Pass formatValue for currency, durations or points; the sign and icon are added for you.
Show sourceexamples/delta-badge/formats.tsx
"use client";
import { DeltaBadge } from "@dethink/components";
const currency = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
notation: "compact",
maximumFractionDigits: 1,
});
export function DeltaBadgeFormats() {
return (
<div className="flex flex-wrap items-center gap-3">
<DeltaBadge value={4.2} comparison="vs last quarter" />
<DeltaBadge
value={18_400}
formatValue={(magnitude) => currency.format(magnitude)}
comparison="vs plan"
/>
<DeltaBadge
value={-37}
formatValue={(magnitude) => `${magnitude} ms`}
positiveDirection="down"
comparison="vs last deploy"
/>
<DeltaBadge
value={3}
formatValue={(magnitude) => `${magnitude} pts`}
size="md"
/>
<DeltaBadge value={null} comparison="vs last month" />
</div>
);
}| Prop | What it does | Default |
|---|---|---|
valuenumber | null | Signed change. The default formatter treats it as percentage points: 12.4 → +12.4%. | Not set |
positiveDirection"up" | "down" | Which direction is good. Use down for churn, latency, cost and errors. | "up" |
neutralThresholdnumber | Changes at or below this magnitude read as flat and neutral. | 0 |
formatValue(magnitude: number) => string | Formats the absolute change. The sign, icon and direction word are added for you. | percent |
comparisonstring | Appended to the screen-reader sentence, e.g. "Up 12.4% vs last month". | Not set |
variant"soft" | "outline" | "plain" | Tinted badge, outlined badge, or inline text with a colored icon. | "soft" |
size"xs" | "sm" | "md" | Matches Badge sizing. | "sm" |
Direction never depends on color: every badge has an arrow icon and a sign, using a true minus. Screen readers hear one sentence, such as “Down 0.8% vs last month”, instead of symbols. Whether a change is good is shown by tone, so name the metric nearby. Text stays in the foreground color for contrast; only the tint and icon carry the status color.