Skip to content
Dethink Components

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 files

Implemented 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.

Usage
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.

Metricsoftoutlineplain
RevenueUp 12.4% vs last monthUp 12.4% vs last monthUp 12.4% vs last month
Active usersDown 3.1% vs last monthDown 3.1% vs last monthDown 3.1% vs last month
ChurnLower is betterDown 0.8% vs last monthDown 0.8% vs last monthDown 0.8% vs last month
p95 latencyLower is betterUp 6.5% vs last monthUp 6.5% vs last monthUp 6.5% vs last month
Error budgetNo change vs last monthNo change vs last monthNo change vs last month
Show sourceexamples/delta-badge/meanings.tsx
examples/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.

Up 4.2% vs last quarterUp $18.4K vs planDown 37 ms vs last deployUp 3 ptsNo change data vs last month
Show sourceexamples/delta-badge/formats.tsx
examples/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>
  );
}
DeltaBadge props
PropWhat it doesDefault
valuenumber | nullSigned 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"
neutralThresholdnumberChanges at or below this magnitude read as flat and neutral.0
formatValue(magnitude: number) => stringFormats the absolute change. The sign, icon and direction word are added for you.percent
comparisonstringAppended 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.