Vanilla Breeze

score-card

Single KPI tile for dashboards and analytics — title, headline value, optional change indicator, sparkline (composed with chart-wc), description, and icon. Wrap in an anchor for drill-down.

Overview

The <score-card> component is the atom of dashboards. It packages a metric tile — title, headline number, optional change indicator, sparkline, description, and icon — into a single composable element that degrades gracefully without JS or CSS.

Score-card is the enhanced form of the semantic stat figure documented at /docs/patterns/data/stats/. Use the pattern when you need a no-JS recipe; use the component when you want consistent layout, theme-driven trend coloring, drill-down affordance, and skeleton loading.

Basic Examples

Minimal use only requires title and value slots. Add change, icon, and a tone attribute to grow the tile into a full KPI card.

Sparkline composition

The sparkline slot accepts any sized element. The slot reserves a fixed height via --score-card-sparkline-height (default 40px) and clips overflow, so the chosen renderer is sized predictably regardless of internal content.

chart-wc with data-size="sparkline" (recommended)

For data-driven trend tiles, compose with <chart-wc> in sparkline mode. The chart strips axes, gridlines, labels, title, legend, and tooltip and fits the slot's reserved height (default 40px). Same component, same data API, no extra wrapper — and the underlying <table> data source still ships an accessible fallback.

Style the trend by overriding --chart-series-1 per trend value — the sparkline shape inherits the colour automatically:

Inline SVG (lightest option, no chart bundle)

If you don't want to ship the optional charts bundle just for tiles, an inline <svg> works the same way. Style with currentColor so it inherits the trend tone via the parent score-card.

chart-wc full chart (for taller trend cards, 120px+)

Drop the data-size="sparkline" attribute and bump the slot height to render a full chart with axes, tooltip, and legend.

Drill-down (link-wrapping)

To make a tile clickable, wrap it in an anchor. The wrapping <a> receives keyboard focus and click — no synthetic key handling, no role override, no tabindex juggling. The whole tile becomes a single accessible link.

When score-card upgrades, it walks ancestors looking for the nearest <a href>. If found, it sets the :state(interactive) hook so component CSS can apply hover and active affordances.

Shadow CSS can't reach the wrapping anchor, so add this partner light-DOM rule once in your stylesheet to apply hover and focus affordances to the tile:

Note: the :state(interactive) hook is set once on upgrade. If you move a score-card into or out of an anchor at runtime, the state won't re-sync — wrap or unwrap before the element connects.

Loading skeleton

Set the loading attribute while data is in flight. Slotted content stays in the DOM so it doesn't reflow on completion; the value, change, and sparkline regions are visually replaced by a shimmer. The animation respects prefers-reduced-motion and falls back to a flat muted background.

Slots

SlotPurposeRequired
title Metric label yes
value Headline number — use <data value="…"> for machine-readable valuesyes
change Delta indicator — recommend nested <data> for the change valueno
sparkline Trend visualisation — compose with <chart-wc> or any sized elementno
description Supporting context line below the metric no
icon Brand or category icon (recommend <icon-wc>) no

Per VB convention, content goes in slots and state goes in attributes. The component is intentionally unopinionated about what fills each slot — pass an SVG, canvas, or chart-wc into the sparkline slot; pass any inline element into value when <data> doesn't fit.

Attributes

AttributeValuesDefaultDescription
trend up, down, flat Drives change-indicator color and exposes :state(trend-up|trend-down|trend-flat)
tone default, success, warning, error, info default Optional accent color applied to the icon slot
layout stack, cluster, compact stack Grid template variant: vertical stack, icon-cluster row, or dense compact
loading boolean absent Skeleton placeholder state via :state(loading)

Internal state hooks

Score-card exposes CustomStateSet entries for CSS targeting via :state():

StateWhen
:state(trend-up) trend="up"
:state(trend-down) trend="down"
:state(trend-flat) trend="flat"
:state(loading) loading attribute present
:state(interactive)Element is wrapped in an <a href> ancestor

CSS Tokens

TokenDefaultPurpose
--score-card-padding var(--size-l) Tile internal padding
--score-card-radius var(--radius-m) Corner radius
--score-card-gap var(--size-s) Gap between rows
--score-card-value-size var(--font-size-3xl) Headline number size
--score-card-value-weight var(--font-weight-bold)Headline number weight
--score-card-sparkline-height 40px Reserved height for sparkline slot
--score-card-surface var(--color-surface) Tile background
--score-card-border 1px solid var(--color-border-subtle)Tile border
--score-card-tone-accent resolved from tone Accent color (icon, optional border)

Static fallback

Before upgrade — and when JS is unavailable — score-card renders its slotted children inline. The recommended fallback structure is the same semantic figure pattern documented at /docs/patterns/data/stats/, so consumers get a fully readable metric without the tile chrome:

Accessibility

  • Semantic native elements throughout. Score-card adds no ARIA roles — meaning comes from the slotted <figure>, <figcaption>, <data>, and <small> structure.
  • Trend is never carried by color alone. Change indicators include the + or sign and a direction word in the copy.
  • Drill-down uses a real anchor. Focus, click activation, right-click menus, and middle-click "open in new tab" all work natively.
  • Reduced motion respected. The loading skeleton's shimmer animation falls back to a static muted background under prefers-reduced-motion.

Related

Stats Pattern

Semantic figure recipe — score-card's no-JS fallback

chart-wc

Recommended sparkline composition partner

data Element

Machine-readable values for value and change slots

Dashboard

Full dashboard layouts using score-card