component — charts

Tracker

A strip of thin cells for visualizing a series of states over time — uptime, incident history, or activity. Status cells keep semantic colors and each cell can carry a tooltip.

  • tracker
  • uptime
  • status
  • data
  • visualization
  • timeline

03 examples

Uptime

Forty cells of service health, in the state tokens because the state is the point: success is operational, warn degraded, danger an outage. Hover a cell for its status.

Sparse activity

Activity is not a state, so it is ink: solid for active, subtle for idle. Empty cells fall back to the raised ground.

Without tooltips

Set `disabledTooltip` for a purely visual strip with no interaction.

source

tracker.tsx

Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.

"use client"

import { useState } from "react"
import { Pressable } from "react-aria-components"
import { Tooltip, TooltipContent } from "@/components/tooltip"
import { cn } from "@/lib/utils"

/**
 * Tracker — quebi design system
 *
 * A horizontal strip of thin cells for visualizing a series of states over
 * time (uptime, incident history, activity heatmaps). Each cell can carry a
 * status color and an optional tooltip. Empty cells are the raised ground, so
 * the strip reads as one ruled band; a status cell uses a state token
 * (`bg-quebi-success` / `-warn` / `-danger`) when its state is semantic, and
 * ink (`bg-quebi-action`) when it only marks that something happened.
 */
interface TrackerBlockProps {
  key?: string | number
  /** Tailwind background class for the cell, e.g. `bg-quebi-success`. */
  color?: string
  tooltip?: string
  /** Background class used when `color` is omitted. */
  defaultBackgroundColor?: string
  disabledTooltip?: boolean
}

const Block = ({
  color,
  tooltip,
  disabledTooltip,
  defaultBackgroundColor = "bg-quebi-raised",
}: TrackerBlockProps) => {
  const [open, setOpen] = useState(false)

  const cell = (
    <div className="size-full overflow-hidden px-px first:ps-0 last:pe-0">
      <div
        className={cn(
          "size-full transition-opacity duration-150",
          color || defaultBackgroundColor,
          "hover:opacity-60",
        )}
      />
    </div>
  )

  return disabledTooltip ? (
    cell
  ) : (
    <Tooltip isOpen={open} onOpenChange={setOpen} delay={0} closeDelay={0}>
      <Pressable onClick={() => setOpen(true)}>{cell}</Pressable>
      <TooltipContent arrow={false} offset={10} placement="top" className="px-2 py-1.5 text-xs">
        {tooltip}
      </TooltipContent>
    </Tooltip>
  )
}

interface TrackerProps
  extends React.ComponentProps<"div">,
    Pick<TrackerBlockProps, "disabledTooltip"> {
  data: TrackerBlockProps[]
  defaultBackgroundColor?: string
}

const Tracker = ({
  data = [],
  disabledTooltip = false,
  defaultBackgroundColor,
  className,
  ref,
  ...props
}: TrackerProps) => {
  return (
    <div ref={ref} className={cn("group flex h-8 w-full items-center", className)} {...props}>
      {data.map((blockProps, index) => (
        <Block
          disabledTooltip={disabledTooltip}
          defaultBackgroundColor={defaultBackgroundColor}
          key={blockProps.key ?? index}
          {...blockProps}
        />
      ))}
    </div>
  )
}

export { Tracker, type TrackerBlockProps, type TrackerProps }