Skip to content
KONIGI Patterns

Dashboards / Labels and context / Explain this metric

‹ 3 of 6 ›

Explain this metric

People need a shared definition of what a metric counts.

Updated September 10, 2026

Problem

People can read the same metric name and assume different definitions, even when they agree on the displayed number.

Solution

Place an accessible explanation beside the metric. State what is counted, the time window, important exclusions, and who maintains the definition.

Use plain language before technical formulas. Make longer detail available without forcing it into a small tooltip.

Keep the definition linked to the calculation so changes are reviewed together. Where possible, reuse one definition across dashboards instead of maintaining separate copies.

Use when

A metric name is ambiguous, the audience has different backgrounds, or the number will be quoted elsewhere.

Don’t use when

The metric is genuinely self-evident to everyone who sees it—a CPU percentage on an engineering dashboard needs no gloss, and adding one dilutes the affordance where it matters.

Trade-offs

Definitions become misleading when queries change without a matching update. Hidden explanations may be missed by the people who need them most. Useful definitions also need someone responsible for maintaining them.

Checklist

  • Does the definition say what is counted in the reader’s language?
  • Does it state the window?
  • Does it list exclusions—internal traffic, bots, test accounts, refunds?
  • Does it name an owner?
  • Is the definition stored with the metric, or retyped per dashboard?
  • When the query changes, what makes the definition change?
  • Is the affordance visible enough that someone who doesn’t know they’re confused might click it?
  • Is the same metric defined identically everywhere it appears?
  • Does the definition distinguish this metric from the similarly-named one next to it?
  • Could this be a modelling-layer definition rather than a tooltip?

Compare

Looker answers this structurally with LookML: metrics are defined in a modelled layer and every chart inherits that definition, so the tooltip is generated rather than written and cannot drift from the query. Grafana has no notion of a metric definition at all—panels carry a description field, and whether it holds a real definition or nothing is a matter of author discipline. Honeycomb sidesteps some of it by keeping raw events: the derivation is visible in the query rather than hidden behind a metric name someone chose months ago. Sentry has an advantage most products lack, in that its core objects are concrete enough to need little definition; an error either happened or it didn’t, and the ambiguity lives in grouping rules rather than in counting.

Data source badge names the source. Ratio and rate needs a clear denominator. Metric targets depends on an agreed definition.

KPI tile presents the value. Hover detail can reveal a short explanation, with another format for longer text.

Explain this metric anatomy A tile with an info affordance in its header and the panel it opens, holding four things: what is counted in the viewer's words, over what window, what is excluded, and who owns the definition. Anatomy Active users i 42,109 Last 7 days Active users WHAT IS COUNTED Anyone who made at least one authenticated request. Not sessions, not page views. OVER WHAT WINDOW A rolling seven days, recomputed hourly. WHAT IS EXCLUDED Internal staff accounts, known bots, and anything from the test tenant. 1 2 3 Owned by growth-analytics 4 1 WHAT IS COUNTED In the viewer's words, not the column name. 2 THE WINDOW Almost every disagreement about a metric turns out to be about the window. 3 EXCLUSIONS Where two honest implementations diverge and neither party finds out. 4 AN OWNER A definition nobody owns is one nobody can correct, and it will be wrong within a year. The failure isn't a wrong number. It's a right one under a definition you don't hold.
Wireframe — the pattern's anatomy, not any one product's version of it

Implementation

Two people read the same tile and mean different things by it. The definition belongs next to the number, not in a wiki nobody opens, and it has to name who owns it so a disagreement has somewhere to go.

shadcn
npx shadcn@latest add popover button card
npm
lucide-react
Tokens
--card--card-foreground--popover--popover-foreground--muted--muted-foreground--border
Active users

42,109

Last 7 days

MetricDefinition.tsxThe definition as four required fields behind an info affordance on the tile: counted, window, excluded, owner.

import { useState } from "react";
import { Info } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";

/**
 * Four fields, all required. A definition is not a sentence; it is what is
 * counted, over what window, with what left out, and who to argue with. Leave
 * any one out and two honest readers of the tile will still disagree.
 */
export type Definition = {
  /** In the reader's words, not the column name. */
  counts: string;
  window: string;
  excludes: string;
  /** A team or a person. A definition nobody owns is one nobody can correct. */
  owner: string;
};

type Props = {
  label: string;
  value: string;
  /** The window the tile shows, which the definition should agree with. */
  range: string;
  definition: Definition;
  onOwnerClick: (owner: string) => void;
  /** Start with the definition showing, as when the tile is the exhibit. */
  defaultOpen?: boolean;
};

export function MetricDefinition({ label, value, range, definition, onOwnerClick, defaultOpen = false }: Props) {
  const [open, setOpen] = useState(defaultOpen);
  const rows: [string, string][] = [
    ["Counts", definition.counts],
    ["Window", definition.window],
    ["Excludes", definition.excludes],
  ];

  return (
    <Card className="w-[220px] p-4">
      {/* A div, not a p: the popover renders inline here in the preview, and
          block content inside a paragraph splits it. */}
      <div className="flex items-center gap-1.5 text-xs uppercase tracking-wide text-muted-foreground">
        {label}
        {/* Non-modal: a definition should not lock the dashboard behind it. */}
        <Popover open={open} onOpenChange={setOpen} modal={false}>
          <PopoverTrigger asChild>
            <Button variant="ghost" size="icon" className="size-5 rounded-full" aria-label={`What ${label} means`}>
              <Info className="size-3.5" />
            </Button>
          </PopoverTrigger>
          <PopoverContent side="right" align="start" className="w-[360px] normal-case tracking-normal">
            <p className="border-b pb-2 text-sm text-popover-foreground">{label}</p>
            <dl className="mt-3 grid grid-cols-[76px_1fr] gap-x-3 gap-y-3 text-xs">
              {rows.map(([k, v]) => (
                <div key={k} className="contents">
                  <dt className="text-muted-foreground">{k}</dt>
                  <dd className="m-0 text-popover-foreground">{v}</dd>
                </div>
              ))}
            </dl>
            <p className="mt-3 flex items-center gap-2 border-t pt-2.5 text-xs text-muted-foreground">
              Owned by
              <button type="button" onClick={() => onOwnerClick(definition.owner)}
                className="rounded-full border px-2 py-0.5 text-popover-foreground hover:bg-muted">
                {definition.owner}
              </button>
            </p>
          </PopoverContent>
        </Popover>
      </div>
      <p className="mt-3 text-3xl font-medium tabular-nums text-card-foreground">{value}</p>
      <p className="mt-2 text-xs text-muted-foreground">{range}</p>
    </Card>
  );
}

demo.tsxHow it is called: Active users over seven days, opened on arrival.

import { MetricDefinition } from "./MetricDefinition";

/**
 * The tile with its definition already open, since the definition is the
 * thing being shown. The owner chip is where a disagreement goes.
 */
export default function Demo() {
  return (
    <div className="min-h-[240px]">
      <MetricDefinition
        label="Active users"
        value="42,109"
        range="Last 7 days"
        definition={{
          counts: "Anyone who made at least one authenticated request. Not sessions, not page views.",
          window: "A rolling seven days, recomputed hourly.",
          excludes: "Internal staff accounts, known bots, and anything from the test tenant.",
          owner: "growth-analytics",
        }}
        onOwnerClick={() => {}}
        defaultOpen
      />
    </div>
  );
}
What it renders. Identical markup in both panes, with only the token values changing.

Examples

Screenshots are grouped by product and include the capture date. Dashed boxes highlight this pattern. Notes appear below each screenshot and when you hover over a box.

Netdata

A large collection of system charts with per-chart controls and anomaly-rate views.

Anomalies / Anomaly advisor September 11, 2026 Netdata Agent, Anomaly advisor (public registry node, signed out) medium · dark · desktop-web
Netdata shows the share and count of measurements flagged as unusual in separate charts. This provides a compact overview across many metrics rather than drawing an expected range on each one. People then select a time period to see which metrics contributed. The empty detail panel explains that a selection is needed before results will appear.
  • Anomaly band A separate anomaly-rate chart summarizes many metrics.
  • Explain this metric Definitions appear directly beneath section headings.
  • Cross-filter Selecting a time period reveals the contributing metrics.
  • Empty state The empty panel explains which action will produce results.
  • Share and embed A report control is available above the charts.
Show 1 more example Hide the rest

Grafana

Examples of panel grids, shared selectors, stat tiles, and chart configuration.

Examples / Dashboard Variables September 10, 2026 Grafana Play (signed out; no version string exposed) medium · dark · desktop-web
This dashboard demonstrates six selectors and explains how their values affect the page. Selected values appear as removable chips, making the active scope easy to read. A text panel also describes a hidden variable that has no visible control. Another note explains that changing the data source clears an instance selection. These dependencies need clear feedback when people change a value.