Aura Design System

Status Dot

A 10px status dot for queued, building, error, ready, and canceled.

Preview

Loading...
import { StatusDot } from "@/components/ui/StatusDot";

export function StatusDotDemo() {
  return (
  <div className="flex flex-col gap-2">
    <div className="flex items-center gap-2">
      <StatusDot state="queued" />
      <StatusDot state="building" />
      <StatusDot state="error" />
      <StatusDot state="ready" />
      <StatusDot state="canceled" />
    </div>
    <div className="flex flex-wrap items-center gap-2">
      <StatusDot state="queued" label />
      <StatusDot state="building" label />
      <StatusDot state="error" label />
      <StatusDot state="ready" label />
      <StatusDot state="canceled" label />
    </div>
  </div>
)
}

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/status-dot

Manual

Install the following dependencies:

pnpm install class-variance-authority

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the StatusDot component into your components/ui/StatusDot.tsx file.

/**
 * @description A 10px status dot for queued, building, error, ready, and canceled.
 */
import * as React from "react";
import { cva } from "class-variance-authority";
import { cn } from "@/utils/class-names";

const STATES = {
  queued: { label: "Queued", message: "Queued." },
  building: { label: "Building", message: "In progress." },
  error: { label: "Error", message: "Failed." },
  ready: { label: "Ready", message: "Ready." },
  canceled: { label: "Canceled", message: "Canceled." },
} as const;

type StatusDotState = keyof typeof STATES;

const dot = cva("inline-block size-[10px] shrink-0 rounded-full", {
  variants: {
    state: {
      queued: "bg-status-dot-queued",
      building: "bg-status-dot-building",
      error: "bg-status-dot-error",
      ready: "bg-status-dot-ready",
      canceled: "bg-status-dot-canceled",
    },
    ring: { true: "ring-2 ring-status-dot-ring", false: "" },
  },
  defaultVariants: { state: "queued", ring: false },
});

interface StatusDotProps extends React.ComponentProps<"span"> {
  state?: StatusDotState;
  /** `true` uses the state name. A string replaces it. */
  label?: boolean | string;
  /** Light ring for overlaying the dot on an avatar. */
  ring?: boolean;
}

function StatusDot({
  state = "queued",
  label,
  ring,
  className,
  ...props
}: StatusDotProps) {
  const s = STATES[state];
  return (
    <span
      data-slot="status-dot"
      data-state={state}
      role="status"
      aria-label={s.label}
      title={s.message}
      className={cn("inline-flex items-center gap-[8px]", className)}
      {...props}
    >
      <span
        data-slot="status-dot-indicator"
        aria-hidden
        className={dot({ state, ring })}
      />
      {label ? (
        <span className="text-sm font-medium text-gray-12">
          {label === true ? s.label : label}
        </span>
      ) : null}
    </span>
  );
}

export { StatusDot, type StatusDotState };

Usage

WithRing

export const WithRing = () => (
  <div className="flex items-center gap-2 rounded-md bg-gray-12 p-2">
    <StatusDot state="ready" ring />
    <StatusDot state="building" ring />
    <StatusDot state="error" ring />
    <StatusDot state="queued" ring />
    <StatusDot state="canceled" ring />
  </div>
)

CustomLabel

export const CustomLabel = () => (
  <StatusDot state="building" label="Deploying" />
)