# Metric Tile (`metric-tile`) - SignalOS widget

> KPI stat tile with trend delta, severity accent, loading skeleton, and a responsive group layout.

- **Version:** 0.1.3
- **Kind:** widget · **Category:** data-display
- **Install:** `npx shadcn@latest add @signalos/metric-tile`
- **Registry dependencies (pulled automatically):** @signalos/tokens, @signalos/utils, @signalos/skeleton
- **npm dependencies:** lucide-react@^1.7.0
- **Files installed:** `src/components/widgets/metric-tile/MetricTile.tsx`, `src/components/widgets/metric-tile/MetricTile.types.ts`

## Access

This is a private registry: pulling source requires a `SIGNALOS_REGISTRY_TOKEN`
(GitHub fine-grained PAT with read access to the signal-widgets repo) and an
`@signalos` entry in components.json `"registries"`. Previews and this document are public.

## Usage

```tsx
import { MetricTile } from "@/components/widgets/metric-tile/MetricTile"
```

## Example

```tsx
// Example: KPI strip with severity accents, deltas, and a chip footer.
import { Activity, AlertTriangle, Inbox } from "lucide-react"

import {
  MetricTile,
  MetricTileGroup,
} from "@/components/widgets/metric-tile/MetricTile"
import { StatusBadge } from "@/components/widgets/status-badge/StatusBadge"

export default function Example() {
  return (
    <MetricTileGroup columns={3}>
      <MetricTile
        label="In flight"
        value={128}
        unit="active slots"
        icon={<Inbox />}
        footer={
          <>
            <StatusBadge severity="critical" appearance="chip">
              3 critical
            </StatusBadge>
            <StatusBadge severity="medium" appearance="chip">
              12 med
            </StatusBadge>
          </>
        }
      />
      <MetricTile
        label="Past due"
        value={6}
        icon={<AlertTriangle />}
        severity="critical"
        // For "bad" metrics, up is bad - flip the delta color:
        delta={{ value: "+2", trend: "up", positiveIsGood: false }}
        onClick={() => console.info("filter: past due")}
      />
      <MetricTile
        label="24h throughput"
        value={342}
        unit="resolved"
        icon={<Activity />}
        delta={{ value: "+12%", trend: "up" }}
      />
    </MetricTileGroup>
  )
}
```

## Props

### `MetricTrend`

```ts
export type MetricTrend = "up" | "down" | "flat"
```

### `MetricSeverity`

```ts
export type MetricSeverity = "critical" | "high" | "medium" | "low" | "info"
```

### `MetricDelta`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | `string` | yes | - | Display string, e.g. "+12%" or "-3". |
| `trend` | `MetricTrend` | yes | - | Direction the arrow points and the color it takes. |
| `positiveIsGood` | `boolean \| undefined` | no | `true` | Whether the trend direction is good news. Controls color: positive up = success, positive down = error; invert for costs etc. |

### `MetricTileProps`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `label` | `string` | yes | - | Eyebrow label above the value, e.g. "In flight". |
| `value` | `string \| number` | yes | - | The headline stat. |
| `unit` | `string \| undefined` | no | - | Small unit/context text next to the value, e.g. "active slots". |
| `icon` | `ReactNode` | no | - | Optional eyebrow icon (any ReactNode, typically a 12px icon). |
| `delta` | `MetricDelta \| undefined` | no | - | Optional trend delta chip under the value. |
| `severity` | `MetricSeverity \| undefined` | no | - | Severity left-border accent. |
| `isLoading` | `boolean \| undefined` | no | - | Renders a skeleton tile while true. |
| `onClick` | `(() => void) \| undefined` | no | - | Makes the tile interactive (button semantics + hover state). |
| `footer` | `ReactNode` | no | - | Free-form row rendered at the bottom of the tile (chips, breakdowns). |
| `className` | `string \| undefined` | no | - |  |
| `data-testid` | `string \| undefined` | no | - | Test identifier rendered as `data-testid` on the root element. |

### `MetricTileGroupProps`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `columns` | `2 \| 4 \| 3 \| undefined` | no | `4` | Column count at desktop width; collapses to 2 on tablet and 1 on mobile. |
| `children` | `ReactNode` | yes | - |  |
| `className` | `string \| undefined` | no | - |  |

## Changelog

# metric-tile

## 0.1.3

- Add a `data-testid` passthrough on the root element so consumers can target the
  tile without relying on markup structure.

## 0.1.2

- Use semantic status tokens (text-text-error, bg-bg-warning, border-border-*, bg-status-*) instead of raw palette scales so client theme overrides restyle status colors.


## 0.1.1

- Content sync fix to bring source in line with published registry payload.


## 0.1.0

- Initial release, genericized from `signal-core-ui` `RoutingSummaryCards`:
  the domain `RoutingDashboardSummary` type is replaced by generic props
  (`label`, `value`, `unit`, `icon`, `delta`, `severity`, `footer`).
- Adds `MetricTileGroup` responsive layout, keyboard-accessible interactive tiles,
  `positiveIsGood` delta coloring, and a skeleton loading state.
