Brand

Status dot

A round status light, with an optional live pulse.

stablev0.1.0 Chromium 103 ui-scale safe

Install

Copies the file into components/ds/, with its registry dependencies and npm packages. See Installation for the one-time setup.
bunx shadcn@3 add @fastrp/status-dot

Examples

Default

Open alone
AçıkKısmen açıkKapalıKontrol ediliyor

API

<StatusDot />

A round status light — one of the few round things in the brand. Pair it with a label: colour alone says nothing to a screen reader or a colour-blind player.

PropTypeDefaultNotes
pulsebooleanfalseA ring that ripples out — for a live state worth noticing. Never on `checking`.
size"sm" | "md" | "lg"—
status"partial" | "online" | "offline" | "checking"—

Dependencies

npm

  • class-variance-authority@^0.7.1

Registry

  • @fastrp/utils

Source

components/ds/status-dot.tsxShow
import { cva, type VariantProps } from 'class-variance-authority';
import type * as React from 'react';
import { cn } from '@/lib/utils';

// @fast-ds [email protected]

const dotVariants = cva('relative inline-flex shrink-0 rounded-full', {
  variants: {
    status: {
      online: 'bg-success',
      /** Some cities up, some not — or a city in maintenance. */
      partial: 'bg-warning',
      offline: 'bg-danger',
      /** Not known yet: no colour claims anything. */
      checking: 'bg-muted-foreground/40',
    },
    size: {
      sm: 'size-1.5',
      md: 'size-2',
      lg: 'size-2.5',
    },
  },
  defaultVariants: { status: 'online', size: 'md' },
});

export type StatusDotProps = Omit<React.ComponentProps<'span'>, 'children'> &
  VariantProps<typeof dotVariants> & {
    /** A ring that ripples out — for a live state worth noticing. Never on `checking`. */
    pulse?: boolean;
  };

/**
 * A round status light — one of the few round things in the brand. Pair it with a label: colour
 * alone says nothing to a screen reader or a colour-blind player.
 */
function StatusDot({ status, size, pulse = false, className, ...props }: StatusDotProps) {
  return (
    <span
      data-slot="status-dot"
      aria-hidden
      className={cn('relative inline-flex', className)}
      {...props}
    >
      {pulse && status !== 'checking' && (
        <span
          className={cn(
            dotVariants({ status, size }),
            'absolute inset-0 animate-ping opacity-60 [animation-duration:calc(1s*var(--motion-scale))]',
          )}
        />
      )}
      <span className={dotVariants({ status, size })} />
    </span>
  );
}

export { StatusDot, dotVariants };