WebberUI

Drilldown Stat Grid

A grid of KPI tiles; clicking a tile expands a full-width detail panel below its row while the remaining tiles move down to make room, and clicking again collapses it.

Loading preview…
npx shadcn@latest add https://webberui.com/r/drilldown-stat-grid.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.

3
0.32
<DrilldownStatGrid />

Installation

npx shadcn@latest add https://webberui.com/r/drilldown-stat-grid.json

Or, once registries are configured in components.json, install it as @webberui/drilldown-stat-grid.

Usage

import {
  DrilldownStatGrid,
  type DrilldownStat,
} from "@/components/ui/drilldown-stat-grid";

const stats: DrilldownStat[] = [
  {
    id: "revenue",
    label: "Revenue this month",
    value: "$32.5k",
    delta: 12.4,
    trend: [18, 22, 20, 26, 24, 30, 33],
    detail: <p>Revenue grew 12.4% over last month, driven mainly by subscription plan renewals.</p>,
  },
  // ...more KPIs
];

<DrilldownStatGrid stats={stats} columns={3} />;

Controlled mode

Passing openId and onOpenChange switches to controlled mode, letting something outside (a URL parameter or another panel, say) decide which tile is expanded:

const [openId, setOpenId] = React.useState<string | null>("revenue");

<DrilldownStatGrid stats={stats} openId={openId} onOpenChange={setOpenId} />;

Without openId it is uncontrolled and manages the state internally, and defaultOpenId can specify which item starts expanded.

Custom detail

renderDetail overrides stat.detail, which suits computing the drilldown content live for each tile:

<DrilldownStatGrid
  stats={stats}
  renderDetail={(stat) => <MyChart metric={stat.id} />}
/>;

Props

DrilldownStatGrid

PropTypeDefaultDescription
statsDrilldownStat[]KPI tile data
columnsnumber3Number of grid columns, deciding where the detail is inserted and how tiles make room
openIdstring | nullControlled mode: the id of the currently expanded tile
defaultOpenIdstring | nullnullThe initially expanded id in uncontrolled mode
onOpenChange(id: string | null) => voidFires on expand / collapse
renderDetail(stat: DrilldownStat) => ReactNodeCustom detail content, overriding stat.detail
durationnumber0.32Expand / collapse duration (seconds)
classNamestringAppended to the grid container
tileClassNamestringAppended to each tile button

DrilldownStat

FieldTypeDescription
idstringUnique key, used to match the row that makes room and the controlled expansion — keep it stable
labelReactNodeKPI name (the small heading at the top left of the tile)
valueReactNodeThe primary number (format it yourself)
unitReactNodeThe unit after the number (at a smaller size)
deltanumberPercentage change; its sign decides the up/down colors and arrow
deltaLabelReactNodeOverrides the change text; when omitted, +delta% is shown
iconReactNodeIcon at the top right of the tile
trendnumber[]Data points for the mini trend chart (drawn only with 2 or more points)
detailReactNodeThe full-width detail content shown once drilled down

How it works

  • The detail block fills the whole row with grid-column: 1 / -1 and is inserted after the last cell of the row containing the clicked tile, so the detail expands in the same place regardless of which cell in that row you click.
  • On expand, the height of the full-width row animates from 0 to auto, and the tiles after it move down smoothly to make room as the layout grows; on collapse the space is released in reverse and the tiles settle back into place.
  • columns decides both the CSS layout and the insertion row computed in JS, and the two must agree — which is why the column count is fixed rather than auto-fill.

Accessibility

  • Each tile is a native <button> carrying aria-expanded and aria-controls; the detail block is role="region" and points back at the triggering tile with aria-labelledby.
  • Keyboard operation is supported: Enter / Space expands or collapses, and Esc collapses and moves focus back to the original tile.
  • When the user has "reduce motion" enabled at the system level, expand / collapse switches instantly and the press scale is disabled, while the layout structure stays unchanged.
  • The trend chart and the arrow icons are all marked aria-hidden, so they do not interfere with assistive technology announcing the values.

On this page