WebberUI

Weighted Metric Wall

A wall of KPI tiles whose sizes are computed automatically from the weight of each value, revealed largest-first in a cascade, expanding a detail layer on hover.

Loading preview…
npx shadcn@latest add https://webberui.com/r/weighted-metric-wall.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

4
2
92
12
0.08
<WeightedMetricWall />

Installation

npx shadcn@latest add https://webberui.com/r/weighted-metric-wall.json

Or, once registries are configured in components.json, install it as @webberui/weighted-metric-wall.

Usage

import { WeightedMetricWall } from "@/components/ui/weighted-metric-wall";

<WeightedMetricWall
  aria-label="Operating metrics this month"
  metrics={[
    {
      label: "Monthly revenue",
      value: "$128K",
      weight: 100,
      delta: 12.4,
      deltaSuffix: "%",
      detail: "Revenue hit a record high, mostly from annual plan renewals.",
    },
    {
      label: "Active users",
      value: "24,860",
      weight: 70,
      delta: 8.1,
      deltaSuffix: "%",
    },
    { label: "Conversion rate", value: "3.8%", weight: 40, delta: -0.6 },
  ]}
/>

Each tile's column and row span is derived automatically from weight (when omitted, it tries to derive a number from value) relative to the maximum: the larger the value, the larger its footprint. On reveal the tiles surface in order from largest to smallest, and on hover or keyboard focus the detail layer expands over the tile.

Props

WeightedMetricWall

PropTypeDefaultDescription
metricsWeightedMetric[]Metric data; each tile's size is computed automatically from its weight
columnsnumber4Number of grid columns (1–6)
maxSpannumber2Maximum column / row span of a single tile (the footprint cap)
rowHeightnumber92Base height of each row (px)
gapnumber12Spacing between tiles (px)
delaynumber0Delay before the reveal starts (seconds)
staggernumber0.08Interval between units as they surface from largest to smallest (seconds)
oncebooleantruePlays only the first time the element enters the viewport
onMetricClick(metric, index) => voidTile click callback; when provided, tiles are presented with button semantics
aria-labelstringLets assistive technology identify what the whole wall is for

WeightedMetric

FieldTypeDescription
labelstringMetric name (the tile's main heading)
valueReactNodeThe primary number; numbers get thousands separators automatically
weightnumberThe weight deciding the footprint size; derived from value when omitted
detailReactNodeThe detail content expanded on hover / focus
deltanumberRelative change: positive = up, negative = down, 0 = flat
deltaSuffixstringSuffix shown after delta (such as "%" or "pt")
unitReactNodeSecondary caption below the value
idstringUnique key; falls back to label as the key when omitted
classNamestringOverrides this tile's class (to add an accent background, say)

How it works

  • The size is derived as ceil((weight / max weight) × maxSpan), clamped to 1..min(maxSpan, columns); tiles use a square span.
  • The grid uses grid-auto-flow: dense, so small tiles backfill the gaps left by large ones and the whole wall packs itself tightly.
  • The order in which tiles surface follows the weight ranking from largest to smallest, with delay and stagger setting the cascade rhythm — independently of DOM order.
  • If the wall sits inside a scroll container, the reveal animation is triggered by whileInView, and once decides whether it replays after leaving the viewport.

Accessibility

  • When the user has "reduce motion" enabled at the system level, the reveal and the detail layer transitions are disabled, so the tiles appear in their final state directly and the detail layer switches instantly.
  • Every tile is keyboard focusable, and focus expands the detail layer just like hover; each tile carries an aria-label that announces its name, value, and trend.
  • The trend arrow icons are marked aria-hidden, and the detail overlay is a visual aid (aria-hidden), so nothing is announced twice alongside the tile label.
  • When onMetricClick is provided, tiles are presented as role="button" and can be triggered with Enter / Space.

On this page