KPI

One number and what it is doing: its change, its shape over time, its progress to a target.

Dark previewReplay previewOpen in new tab

Newsletter

Last 30 days
Subscribers
18,240
Unsubscribe rate
0.8%
Open rate
47.3%

Use it for a number someone checks, with whatever says how it is doing: the change since last time, the shape of it over a period, how far it is towards a target.

The number comes first. The trend, the chart and the bar are all small on purpose, and none of them can be read to a value.

For a chart someone needs to read values from, use a chart with an axis. For one bare figure with a label, a KpiTitle and a KpiValue are enough.

Installation

Copy the folder into components/ui, where you can edit it. It needs the cn helper and the theme's info, success and warning colours.

text
components/ui/kpi/
├── Kpi.vue
├── KpiHeader.vue … KpiGroup.vue
└── index.ts

Usage

vue
<script setup lang="ts">
import { Kpi, KpiTitle, KpiTrend, KpiValue } from "@/components/ui/kpi";
</script>

<template>
  <Kpi>
    <KpiTitle>Subscribers</KpiTitle>
    <KpiValue>18,240</KpiValue>
    <KpiTrend :value="6.4" caption="this month" />
  </Kpi>
</template>

Composition

<Kpi>
  <KpiHeader>
    <KpiIcon />
    <KpiTitle />
    <KpiActions />
  </KpiHeader>
  <KpiContent>
    <KpiStat>
      <KpiValue />
      <KpiTrend />
    </KpiStat>
    <KpiChart />
  </KpiContent>
  <KpiProgress />
  <KpiFooter />
</Kpi>
  • KpiHeader: the row over the number. A title in it takes the room between the icon and the actions.
  • KpiIcon: a small tile for an icon. tone colours it as good or bad news.
  • KpiTitle: what the number measures.
  • KpiActions: whatever belongs at the end of the header: a period, a trend badge, a button.
  • KpiContent: the row the number is in. layout="inline" is for a chart beside it.
  • KpiStat: title, value and trend stacked as one block, for a row with a chart at its other end.
  • KpiValue: the number.
  • KpiTrend: the change, given as a number. It writes the sign and picks the colour.
  • KpiChart: a sparkline of a list of numbers. inline gives it a fixed box, filled a wash under the line.
  • KpiProgress: a bar towards a target, with a label and the percentage over it.
  • KpiFooter: a quiet line under everything.
  • KpiSeparator: a rule between two parts of one card.
  • KpiGroup: several metrics as one panel, side by side or stacked, with a rule between each and the next. surface draws it as a card of its own.

A card of its own

surface draws the metric as a card. The trend is a badge in the header, and the chart runs the width of the card under the number with a wash beneath the line.

Dark previewReplay previewOpen in new tab
Weekly readers
4,120
Counted every Monday

Towards a target

KpiProgress takes a value and what it is out of. KpiIcon takes a tone, so the tile can agree with the news beside it.

Dark previewReplay previewOpen in new tab

Targets

This quarter
Paid members
1,463
of 2,00073%
38 days left in the quarter
Sponsorship
€9,800
of €24,00041%

Several as one panel

KpiGroup puts a rule between metrics and shares the width between them. Without a header each metric is a title, a value and a trend.

Dark previewReplay previewOpen in new tab
Sent
112
Replies
386
Bounces
0.6%

API Reference

Kpi

PropTypeDefaultDescription
surfacebooleanfalseDraws the metric as a card of its own. Leave it off inside a frame or a group.

KpiIcon

PropTypeDefaultDescription
tone"good" | "bad" | "flat" | "neutral""neutral"How the tile is coloured: by good or bad news, or left neutral.

KpiContent

PropTypeDefaultDescription
layout"below" | "inline""below"inline puts a chart beside the number in a fixed box; below leaves it under everything.

KpiTrend

PropTypeDefaultDescription
valuenumber—The change. Its sign decides the direction.
format(value: number) => string—Writes the change. Defaults to a signed percentage with one decimal: "+7.8%".
goodDirection"up" | "down" | "none""up"Which direction is the good news. down for churn, latency, cost; none for no colour.
variant"text" | "badge""text"text is a line of colour; badge puts a pill and an arrow round it.
captionstring—Quiet words after the change: "this month".
thresholdnumber0A change this small or smaller is drawn as no change.

KpiChart

PropTypeDefaultDescription
datanumber[]—The values, oldest first.
color"primary" | "info" | "success" | "warning" | "destructive""primary"Which of the theme's roles the line is drawn in.
filledbooleanfalseFills under the line with a wash of the same colour.
heightnumber—How tall the chart is, in pixels.
inlinebooleanfalseA fixed box for the end of a layout="inline" row. Charts beside numbers of different lengths are then all one width, and line up down a column.
strokeWidthnumber2How thick the line is, in pixels.

KpiProgress

PropTypeDefaultDescription
valuenumber—How far along.
maxValuenumber100What value is out of.
labelstring—Words at the start of the row over the bar: "of 5,000".
showValueLabelbooleanfalseWrites the percentage at the end of the row over the bar.
color"primary" | "info" | "success" | "warning" | "destructive""primary"Which of the theme's roles the bar is drawn in.
SlotDescription
labelMarkup for the label, in place of the label prop.

KpiGroup

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Side by side with upright rules, or stacked with level ones.
surfacebooleanfalseDraws the group as a card of its own, for a group not already inside one.

Every part also takes the attributes of the element it renders, and a class.

Notes

Trend colours

KpiTrend is drawn in the success colour when the number moved the good way, in the destructive colour when it moved the bad way, and in the muted colour when it did not move. Up is good unless you say otherwise: pass good-direction="down" for a metric where less is better, or "none" for one where neither is.

threshold is the size of change that still counts as none. With :threshold="0.5", a change of 0.3 is drawn flat.

Writing the change

The change is written as a signed percentage with one decimal. format is given the value and returns the text, for a change in another unit. caption adds quiet words after it and is read out with it.

The chart

data is a list of numbers, oldest first. The line is scaled between the lowest and highest of them, so it shows the shape of the change and says nothing about its size. That is why it has no axis.

inline fixes the chart at 128 by 56 pixels. In a column of cards with titles of different lengths, the charts are then all one width and line up down the right-hand edge.

Colour

color on KpiChart and KpiProgress names one of the theme's roles and defaults to primary.

In a group

KpiGroup pads its children on the sides that face a rule. Put Kpi elements straight inside it. In a horizontal group the metrics share the width equally, so keep titles to a word or two, or stack them with orientation="vertical".

Server rendering

KPI holds no state, so it renders the same on the server as in the browser and adds no work after hydration.