DEV3 Technologies home

Admin stat cards with trend badges

A row of KPI cards showing a label, a large value and a coloured up or down trend badge with an accessible description.

Tailwind CSSDashboard widgetsstatskpibadgedashboard

Live preview

Code

<dl class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
  <div class="rounded-xl border border-slate-200 bg-white p-5">
    <dt class="truncate text-sm text-slate-500">Revenue</dt>
    <dd class="mt-2 flex items-baseline justify-between">
      <span class="text-2xl font-semibold tabular-nums text-slate-900">$48,200</span>
      <span class="inline-flex items-center gap-1 rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700">
        <svg class="size-3" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M6 2 10.5 8h-9L6 2Z"/></svg>
        <span class="sr-only">Increased by</span>12.4%
      </span>
    </dd>
  </div>

  <div class="rounded-xl border border-slate-200 bg-white p-5">
    <dt class="truncate text-sm text-slate-500">New customers</dt>
    <dd class="mt-2 flex items-baseline justify-between">
      <span class="text-2xl font-semibold tabular-nums text-slate-900">1,284</span>
      <span class="inline-flex items-center gap-1 rounded-full bg-red-50 px-2 py-0.5 text-xs font-medium text-red-700">
        <svg class="size-3" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M6 10 1.5 4h9L6 10Z"/></svg>
        <span class="sr-only">Decreased by</span>3.1%
      </span>
    </dd>
  </div>

  <div class="rounded-xl border border-slate-200 bg-white p-5">
    <dt class="truncate text-sm text-slate-500">Refunds</dt>
    <dd class="mt-2 flex items-baseline justify-between">
      <span class="text-2xl font-semibold tabular-nums text-slate-900">37</span>
      <!-- a falling refund count is good news, so it is green -->
      <span class="inline-flex items-center gap-1 rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700">
        <svg class="size-3" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M6 10 1.5 4h9L6 10Z"/></svg>
        <span class="sr-only">Decreased by</span>8.0%
      </span>
    </dd>
  </div>

  <div class="rounded-xl border border-slate-200 bg-white p-5">
    <dt class="truncate text-sm text-slate-500">Active sessions</dt>
    <dd class="mt-2 flex items-baseline justify-between">
      <span class="text-2xl font-semibold tabular-nums text-slate-900">902</span>
      <span class="rounded-full bg-slate-100 px-2 py-0.5 text-xs font-medium text-slate-600">
        <span class="sr-only">No change</span>0%
      </span>
    </dd>
  </div>
</dl>

How it works

What it solves

Dashboards open with a few headline numbers, and each needs a trend so readers can tell good from bad. Colour alone fails colour blind users, so this card pairs colour with an arrow and a text alternative.

How it works

The cards sit in a dl inside a responsive grid, using dt for the label and dd for the value, which gives the data real term and definition semantics. The trend badge is a small pill with a green or red palette, an inline arrow SVG that is aria-hidden, and the percentage. A visually hidden span says "Increased by" or "Decreased by", so screen readers hear the direction. A fourth card shows a neutral state in slate for flat metrics. Numbers use tabular-nums so digits line up when values change, and truncate keeps long labels inside the card.

Adapting it

Render the cards from your data in a loop and choose the badge classes by sign. Some metrics are good when they fall, such as refunds; pass a flag so a decrease can still be green. Add a sparkline by placing an SVG in the card's right corner.

Pitfalls

  • Format numbers on the server with the user's locale, not in the template.
  • Do not hard code the colour from the sign; cost metrics invert it.
  • Keep the arrow and the hidden text consistent with the number.

How to use it

  1. Paste stat-cards.html inside your dashboard content area in a Tailwind 4 project.
  2. Loop your metrics and fill label, value and change.
  3. Use the green classes for good news and the red classes for bad news, independent of sign.
  4. Drop the neutral card if you do not track flat values.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets