Components

Provider legend row

Horizontally scrollable pill row used above provider-heavy charts (Approval rate by provider, Error rate, Latency p90/p95/p99, Timeout rate). Each pill stacks [dot + name muted] then [value 2xl semibold tabular]. When onSelectKey is wired, pills become buttons — clicking one toggles it selected; other pills dim to opacity-40 so the chart consumer can filter to a single provider. Click the same pill again to clear.

Updated Sep 24, 2026 by Juan Pablo Turina

Anatomy

A horizontally scrollable row of pills. The outer container bleeds -mx-6 px-6 so the first/last pill isn't clipped by the ChartCard's p-6. Each pill has two rows: first = dot + name (text-sm muted); second = value (text-2xl semibold tabular-nums foreground). min-w-28 (112px) per pill so widths stay consistent.

Stripe
89.13%
Adyen
84.51%
Braintree
79.20%
dLocal
75.02%
Cybersource
71.44%
PayU
68.31%
Worldpay
64.90%
Checkout.com
62.11%
  1. 1
    Scroll container

    -mx-6 overflow-x-auto px-6 pb-2. Lets 6+ providers scroll horizontally inside a ChartCard without clipping.

  2. 2
    Pill (static)

    min-w-28 flex-col gap-1 px-2 py-1. Used when onSelectKey is not defined. Pure <div>.

  3. 3
    Pill (interactive)

    Same layout but rendered as a <button> with aria-pressed. Hover = bg-accent. Selected = bg-muted. When any pill is selected, unselected pills get opacity-40.

  4. 4
    Dot

    Color from provider.color. The recipes mirror production's colours; see their note.

  5. 5
    Name

    text-sm text-muted-foreground. Truncates with min-w-0 sibling.

  6. 6
    Value

    text-2xl font-semibold tabular-nums text-foreground. Current KPI (%, ms).

Recipes

Static (display-only)Production mirror

Production mirror: the Insights provider charts as they ship today, eight providers on one chart with production's colours. Use it only to rebuild those cards. In a new flow a line chart carries up to three series; more providers than that is a table. Omit onSelectKey. Pills render as <div>s with no hover / click behavior. Use when the chart doesn't support filter-to-one-provider or when the row is purely informational.

Stripe
89.13%
Adyen
84.51%
Braintree
79.20%
dLocal
75.02%
Cybersource
71.44%
PayU
68.31%
Worldpay
64.90%
Checkout.com
62.11%
<ProviderLegendRow providers={providers} />
// providers: ProviderLegendItem[] = [
//   { key, name, color, value },
// ]
Interactive (click to filter)Production mirror

Production mirror: same as above, with click to filter. In a new flow, the same limit: up to three series on the chart. Wire selectedKey + onSelectKey to enable click-to-filter. The chart consumer reads selectedKey and filters its series to the matching provider (or renders all when null). Selection dims non-selected pills to opacity-40.

const [selected, setSelected] = React.useState<string | null>(null);

<ProviderLegendRow
  providers={providers}
  selectedKey={selected}
  onSelectKey={setSelected}
/>
// Read `selected` in the sibling chart to filter its series.

Import

Copy this import line at the top of the file where you compose this atom.

ProviderLegendRow is controlled. State (selectedKey) belongs to the parent so both the row AND the sibling chart can react to it. Toggling a pill twice clears the selection back to null.
import { ProviderLegendRow, type ProviderLegendItem } from "@/components/ui/provider-legend-row";

Props

Everything else from the underlying HTML or Radix primitive is forwarded via ...props.

ProviderLegendRow

PropTypeDefaultDescription
providersProviderLegendItem[] (required)—The pills to render. See item shape below.
selectedKeystring | null—Selected provider key. Pass null for no selection. When defined together with `onSelectKey`, pills become interactive buttons.
onSelectKey(key: string | null) => void—Fires when the user toggles a pill. Receives the new key, or null to clear. Absent = pills stay static.
classNamestring—Extra classes on the outer overflow-x-auto wrapper.

ProviderLegendItem

PropTypeDefaultDescription
keystring (required)—Stable key. Used for React reconciliation AND for `selectedKey` matching.
namestring (required)—Provider name (dot + name row, muted).
colorstring (required)—Dot color: var(--series-N) by entity in a new flow. The recipes keep production's --chart-* as mirrors.
valuestring (required)—Current KPI ("89.13%", "1,287 ms"). Rendered text-2xl semibold tabular-nums.

Related

Cross-links to atoms and patterns you may reach for next.

  • Status legendStatic wrap-friendly sibling. Use when the legend should stack instead of scroll and interactivity isn't needed.
  • Chart cardCanonical host — sits at the top of the ChartCard body above the chart.
  • ChartThe chart consumer reads selectedKey and filters its series accordingly.