Skip to content

Service status overview

Per-service health with a summary derived from the services below it.

Applicationstarterdashboardstatusuptimeservices

Live preview

full widthLive preview — open it in a new tab for the full-height version.
Open the preview in a new tab

Source

This exact file renders the preview above.

import { Panel } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
import { Status } from '@/components/ui/status'
import { Divider } from '@/components/ui/layout'

/**
 * Service status overview
 *
 * Per-service health plus a rolled-up summary. The summary is derived from the
 * services rather than typed in, so it can never claim "all systems normal"
 * while a row below it says otherwise.
 */
const services = [
  { name: 'api.foundry.dev', kind: 'operational' as const, label: 'Operational', uptime: 99.98 },
  { name: 'cdn.foundry.dev', kind: 'operational' as const, label: 'Operational', uptime: 99.99 },
  {
    name: 'webhooks.foundry.dev',
    kind: 'degraded' as const,
    label: 'Elevated latency',
    uptime: 99.42,
  },
  { name: 'search.foundry.dev', kind: 'operational' as const, label: 'Operational', uptime: 99.95 },
  { name: 'exports.foundry.dev', kind: 'pending' as const, label: 'Maintenance', uptime: 99.1 },
]

export default function DashboardStatusOverview() {
  const degraded = services.filter((service) => service.kind !== 'operational')
  const summaryKind = degraded.length === 0 ? 'operational' : 'degraded'
  const summaryLabel =
    degraded.length === 0
      ? 'All systems operational'
      : `${degraded.length} ${degraded.length === 1 ? 'service' : 'services'} need attention`

  return (
    <section className="bg-canvas p-4 sm:p-6">
      <Panel
        title="Service status"
        description="Rolled up from the services below"
        headingLevel="h2"
        action={<Status appearance="pill" kind={summaryKind} label={summaryLabel} />}
      >
        <ul className="flex flex-col gap-4">
          {services.map((service, index) => (
            <li key={service.name}>
              {index > 0 ? <Divider weight="subtle" className="mb-4" /> : null}
              <div className="flex flex-wrap items-center justify-between gap-3">
                <Status appearance="dot" kind={service.kind} label={service.name} />
                <div className="flex items-center gap-3">
                  <Badge size="sm" tone={service.kind === 'operational' ? 'success' : 'warning'}>
                    {service.label}
                  </Badge>
                  <span className="font-mono text-xs text-ink-muted tabular-nums">
                    {service.uptime.toFixed(2)}%
                  </span>
                </div>
              </div>
              <Progress
                value={service.uptime}
                size="sm"
                tone={service.kind === 'operational' ? 'success' : 'warning'}
                className="mt-2"
                srLabel={`${service.name} uptime`}
              />
            </li>
          ))}
        </ul>
      </Panel>
    </section>
  )
}

components/blocks/sections/dashboard/status-overview.tsx

import { Panel } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
import { Status } from '@/components/ui/status'
import { Divider } from '@/components/ui/layout'

/**
 * Service status overview
 *
 * Per-service health plus a rolled-up summary. The summary is derived from the
 * services rather than typed in, so it can never claim "all systems normal"
 * while a row below it says otherwise.
 */
const services = [
  { name: 'api.foundry.dev', kind: 'operational' as const, label: 'Operational', uptime: 99.98 },
  { name: 'cdn.foundry.dev', kind: 'operational' as const, label: 'Operational', uptime: 99.99 },
  {
    name: 'webhooks.foundry.dev',
    kind: 'degraded' as const,
    label: 'Elevated latency',
    uptime: 99.42,
  },
  { name: 'search.foundry.dev', kind: 'operational' as const, label: 'Operational', uptime: 99.95 },
  { name: 'exports.foundry.dev', kind: 'pending' as const, label: 'Maintenance', uptime: 99.1 },
]

export default function DashboardStatusOverview() {
  const degraded = services.filter((service) => service.kind !== 'operational')
  const summaryKind = degraded.length === 0 ? 'operational' : 'degraded'
  const summaryLabel =
    degraded.length === 0
      ? 'All systems operational'
      : `${degraded.length} ${degraded.length === 1 ? 'service' : 'services'} need attention`

  return (
    <section className="bg-canvas p-4 sm:p-6">
      <Panel
        title="Service status"
        description="Rolled up from the services below"
        headingLevel="h2"
        action={<Status appearance="pill" kind={summaryKind} label={summaryLabel} />}
      >
        <ul className="flex flex-col gap-4">
          {services.map((service, index) => (
            <li key={service.name}>
              {index > 0 ? <Divider weight="subtle" className="mb-4" /> : null}
              <div className="flex flex-wrap items-center justify-between gap-3">
                <Status appearance="dot" kind={service.kind} label={service.name} />
                <div className="flex items-center gap-3">
                  <Badge size="sm" tone={service.kind === 'operational' ? 'success' : 'warning'}>
                    {service.label}
                  </Badge>
                  <span className="font-mono text-xs text-ink-muted tabular-nums">
                    {service.uptime.toFixed(2)}%
                  </span>
                </div>
              </div>
              <Progress
                value={service.uptime}
                size="sm"
                tone={service.kind === 'operational' ? 'success' : 'warning'}
                className="mt-2"
                srLabel={`${service.name} uptime`}
              />
            </li>
          ))}
        </ul>
      </Panel>
    </section>
  )
}

components/ui/status.tsx

import { CheckCircle2, AlertTriangle, XCircle, Info, CircleDashed, Clock } from 'lucide-react'
import { cn } from '@/lib/cn'

/**
 * Status
 *
 * Deliberately separate from Badge. A status describes the *state of a thing*
 * (a deployment, an invoice, a service) and therefore always pairs an icon
 * with a label — colour is the third signal, never the only one. That rule is
 * what makes the library legible to colour-blind users without a theme switch.
 */
export type StatusKind = 'operational' | 'degraded' | 'down' | 'pending' | 'info' | 'idle'

export interface StatusProps {
  kind: StatusKind
  label: string
  /** `dot` for dense tables, `pill` for standalone display. */
  appearance?: 'dot' | 'pill' | 'inline'
  className?: string
}

const config = {
  operational: {
    icon: CheckCircle2,
    text: 'text-success',
    bg: 'bg-success-soft border-success-line',
    dot: 'bg-success',
  },
  degraded: {
    icon: AlertTriangle,
    text: 'text-warning',
    bg: 'bg-warning-soft border-warning-line',
    dot: 'bg-warning',
  },
  down: {
    icon: XCircle,
    text: 'text-danger',
    bg: 'bg-danger-soft border-danger-line',
    dot: 'bg-danger',
  },
  pending: { icon: Clock, text: 'text-info', bg: 'bg-info-soft border-info-line', dot: 'bg-info' },
  info: { icon: Info, text: 'text-info', bg: 'bg-info-soft border-info-line', dot: 'bg-info' },
  idle: {
    icon: CircleDashed,
    text: 'text-ink-subtle',
    bg: 'bg-surface-sunken border-line',
    dot: 'bg-ink-subtle',
  },
} as const

export function Status({ kind, label, appearance = 'inline', className }: StatusProps) {
  const entry = config[kind]
  const Icon = entry.icon

  if (appearance === 'dot') {
    return (
      <span className={cn('inline-flex items-center gap-2 text-sm text-ink', className)}>
        <span className={cn('size-2 shrink-0 rounded-full', entry.dot)} aria-hidden="true" />
        {label}
      </span>
    )
  }

  if (appearance === 'pill') {
    return (
      <span
        className={cn(
          'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium',
          entry.bg,
          entry.text,
          className,
        )}
      >
        <Icon className="size-3.5 shrink-0" aria-hidden="true" />
        {label}
      </span>
    )
  }

  return (
    <span
      className={cn('inline-flex items-center gap-1.5 text-sm font-medium', entry.text, className)}
    >
      <Icon className="size-4 shrink-0" aria-hidden="true" />
      {label}
    </span>
  )
}

components/ui/progress.tsx

import { cn } from '@/lib/cn'

/**
 * Progress
 *
 * Determinate by default. When `value` is omitted the bar renders as
 * indeterminate and drops `aria-valuenow`, which is what tells assistive tech
 * "in progress, duration unknown" rather than "0%".
 */
export interface ProgressProps {
  /** 0–100. Omit for an indeterminate bar. */
  value?: number
  label?: string
  /** Renders the numeric value beside the label. */
  showValue?: boolean
  /** Accessible name when no visible label is wanted. */
  srLabel?: string
  tone?: 'accent' | 'success' | 'warning' | 'danger'
  size?: 'sm' | 'md'
  className?: string
}

const tones = {
  accent: 'bg-accent',
  success: 'bg-success',
  warning: 'bg-warning',
  danger: 'bg-danger',
} as const

export function Progress({
  value,
  label,
  showValue = false,
  srLabel,
  tone = 'accent',
  size = 'md',
  className,
}: ProgressProps) {
  const clamped = value === undefined ? undefined : Math.max(0, Math.min(100, Math.round(value)))

  return (
    <div className={cn('w-full', className)}>
      {(label || showValue) && (
        <div className="mb-1.5 flex items-baseline justify-between gap-3">
          {label ? <span className="text-xs font-medium text-ink">{label}</span> : <span />}
          {showValue && clamped !== undefined ? (
            <span className="font-mono text-xs text-ink-muted tabular-nums">{clamped}%</span>
          ) : null}
        </div>
      )}
      <div
        role="progressbar"
        aria-valuemin={0}
        aria-valuemax={100}
        aria-valuenow={clamped}
        aria-label={label ? undefined : (srLabel ?? 'Progress')}
        aria-valuetext={clamped === undefined ? 'In progress' : `${clamped}%`}
        className={cn(
          'w-full overflow-hidden rounded-full bg-surface-sunken',
          size === 'sm' ? 'h-1' : 'h-2',
        )}
      >
        {clamped === undefined ? (
          <div className={cn('h-full w-2/5 animate-pulse-token rounded-full', tones[tone])} />
        ) : (
          <div
            className={cn(
              'h-full rounded-full transition-[width] duration-300 ease-standard',
              tones[tone],
            )}
            style={{ width: `${clamped}%` }}
          />
        )}
      </div>
    </div>
  )
}

export interface ProgressRingProps {
  value: number
  size?: number
  label?: string
  tone?: keyof typeof tones
  className?: string
}

/** Circular variant, for dashboard tiles where a bar would waste width. */
export function ProgressRing({
  value,
  size = 56,
  label,
  tone = 'accent',
  className,
}: ProgressRingProps) {
  const clamped = Math.max(0, Math.min(100, Math.round(value)))
  const stroke = 5
  const radius = (size - stroke) / 2
  const circumference = 2 * Math.PI * radius
  const offset = circumference - (clamped / 100) * circumference
  const strokeColor = {
    accent: 'var(--color-accent)',
    success: 'var(--color-success)',
    warning: 'var(--color-warning)',
    danger: 'var(--color-danger)',
  }[tone]

  return (
    <div
      className={cn('relative inline-flex items-center justify-center', className)}
      role="progressbar"
      aria-valuemin={0}
      aria-valuemax={100}
      aria-valuenow={clamped}
      aria-label={label ?? 'Progress'}
    >
      <svg width={size} height={size} className="-rotate-90" aria-hidden="true">
        <circle
          cx={size / 2}
          cy={size / 2}
          r={radius}
          fill="none"
          stroke="var(--color-surface-sunken)"
          strokeWidth={stroke}
        />
        <circle
          cx={size / 2}
          cy={size / 2}
          r={radius}
          fill="none"
          stroke={strokeColor}
          strokeWidth={stroke}
          strokeLinecap="round"
          strokeDasharray={circumference}
          strokeDashoffset={offset}
        />
      </svg>
      <span className="absolute font-mono text-xs font-medium tabular-nums">{clamped}%</span>
    </div>
  )
}

Demo source — adapt to your project. Foundry is not published as a package.

Usage

The summary is computed from the services rather than typed in, so it can never claim 'all systems normal' while a row below it says otherwise.

  • Derive rolled-up status; a hand-maintained summary will eventually lie.
  • Pair every uptime bar with the numeric figure.

Variants and states

Every entry below is a genuine difference in behaviour or layout, and every one of them is visible in the preview above.

  • Five services
  • Derived summary
  • Per-service uptime bars

Accessibility

Derived summary
The headline status is computed, so it cannot contradict the list.
Named progress
Each uptime bar carries an accessible name without duplicating visible text.

Foundry implements published ARIA patterns and is tested against them. No WCAG certification is claimed — see the accessibility documentation for what is and is not covered.