Componentfeedback3.43 KB gzipZero dependencies
Status Dot
Live status beacon with radar sweep pulse, glow rings and occupancy levels.
Preview
Live Preview
NORTH TURNSTILEGATE A-04
TRN-902GENERAL ADMISSION
LOW OCCUPANCY22%
RAPID ADMISSION · FLOWING
0% EMPTY50% FLOW100% MAX
WAIT TIME< 1 MIN
THROUGHPUT46 / MIN
BEACONLIVE RADAR
NORTH TURNSTILEGATE A-04
TRN-902GENERAL ADMISSION
CAPACITY FULL82%
PEAK CONGESTION · GATES DIVERTED
0% EMPTY50% FLOW100% MAX
WAIT TIME12 min
THROUGHPUT340/hr
BEACONLIVE RADAR
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add status-dotOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { StatusDot } from "@/components/ui/status-dot";
<StatusDot level="full" occupancyPercent={82} waitTime="12 min" />Usage guidance
Use Status Dot when a ticket stub surface needs Live status beacon with radar sweep pulse, glow rings and occupancy levels. Keep props minimal and prefer composition with sibling stubs.
Avoid Status Dot for long form flows or dense data grids where a dedicated layout block fits better.
Source code
status-dot.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export type GateOccupancyLevel = "low" | "moderate" | "full" | "closed";
export type StatusDotVariant = "booth" | "badge" | "dot";
export type StatusDotSize = "sm" | "md" | "lg";
export interface StatusDotProps extends React.HTMLAttributes<HTMLDivElement> {
/** Gate occupancy level */
level?: GateOccupancyLevel;
/** Display variant: complete booth card, inline ticket badge, or pure beacon dot */
variant?: StatusDotVariant;
/** Size of the beacon dot */
size?: StatusDotSize;
/** Name or designation of the turnstile gate */
gateName?: string;
/** Terminal or booth station title */
terminal?: string;
/** Lane category (e.g. VIP FAST-TRACK, GENERAL ADMISSION) */
laneType?: string;
/** Numerical occupancy percentage override (0 to 100) */
occupancyPercent?: number;
/** Estimated wait duration string */
waitTime?: string;
/** Admission flow rate */
throughput?: string;
/** Turnstile equipment serial or station ID */
turnstileId?: string;
/** Custom label override for the status */
label?: string;
/** Custom secondary description override */
sublabel?: string;
/** Whether the radar sweep beam is active */
radarSweep?: boolean;
/** Whether glow ring pulses are animated */
pulse?: boolean;
/** Additional CSS class names */
className?: string;
}
interface LevelConfig {
label: string;
sublabel: string;
defaultPercent: number;
defaultWait: string;
defaultThroughput: string;
dotColor: string;
dotBg: string;
textColor: string;
badgeBorder: string;
badgeBg: string;
glowClass: string;
sweepDuration: string;
meterBg: string;
}
const levelConfigs: Record<GateOccupancyLevel, LevelConfig> = {
low: {
label: "LOW OCCUPANCY",
sublabel: "RAPID ADMISSION · FLOWING",
defaultPercent: 22,
defaultWait: "< 1 MIN",
defaultThroughput: "46 / MIN",
dotColor: "text-success",
dotBg: "bg-success",
textColor: "text-success",
badgeBorder: "border-success/40",
badgeBg: "bg-success/10",
glowClass: "shadow-[0_0_12px_rgba(16,185,129,0.55)]",
sweepDuration: "3.6s",
meterBg: "bg-success",
},
moderate: {
label: "MODERATE FLOW",
sublabel: "STEADY ENTRY · MINOR QUEUE",
defaultPercent: 58,
defaultWait: "4-6 MIN",
defaultThroughput: "32 / MIN",
dotColor: "text-warning",
dotBg: "bg-warning",
textColor: "text-warning",
badgeBorder: "border-warning/40",
badgeBg: "bg-warning/10",
glowClass: "shadow-[0_0_12px_rgba(245,158,11,0.55)]",
sweepDuration: "2.6s",
meterBg: "bg-warning",
},
full: {
label: "CAPACITY FULL",
sublabel: "PEAK CONGESTION · GATES DIVERTED",
defaultPercent: 94,
defaultWait: "12-15 MIN",
defaultThroughput: "14 / MIN",
dotColor: "text-destructive",
dotBg: "bg-destructive",
textColor: "text-destructive",
badgeBorder: "border-destructive/40",
badgeBg: "bg-destructive/10",
glowClass: "shadow-[0_0_14px_rgba(244,63,94,0.65)]",
sweepDuration: "1.6s",
meterBg: "bg-destructive",
},
closed: {
label: "GATE CLOSED",
sublabel: "TURNSTILE LOCKED · USE GATE B",
defaultPercent: 0,
defaultWait: "LOCKED",
defaultThroughput: "0 / MIN",
dotColor: "text-muted-foreground",
dotBg: "bg-muted-foreground/60",
textColor: "text-muted-foreground",
badgeBorder: "border-border",
badgeBg: "bg-secondary/60",
glowClass: "shadow-none",
sweepDuration: "0s",
meterBg: "bg-muted-foreground/30",
},
};
/**
* Radar Beacon Visual Element
*/
function BeaconGraphic({
level,
size = "md",
radarSweep = true,
pulse = true,
}: {
level: GateOccupancyLevel;
size?: StatusDotSize;
radarSweep?: boolean;
pulse?: boolean;
}) {
const config = levelConfigs[level];
const isClosed = level === "closed";
const sizeContainer = {
sm: "size-5",
md: "size-8",
lg: "size-14",
}[size];
const sizeDot = {
sm: "size-2",
md: "size-3",
lg: "size-4",
}[size];
return (
<div
className={cn(
"relative flex items-center justify-center shrink-0 select-none",
sizeContainer,
config.dotColor
)}
>
{/* Outer reticle / sweep track */}
<div
className={cn(
"absolute inset-0 rounded-full border border-current/20 transition-all",
size === "lg" && "border-current/30"
)}
/>
{/* Internal concentric guide ring for lg and md */}
{size !== "sm" && (
<div className="absolute inset-[25%] rounded-full border border-dashed border-current/25" />
)}
{/* Reticle Crosshair Ticks for lg */}
{size === "lg" && (
<>
<div className="absolute top-0 bottom-0 left-1/2 -translate-x-1/2 w-[1px] bg-current/15" />
<div className="absolute left-0 right-0 top-1/2 -translate-y-1/2 h-[1px] bg-current/15" />
</>
)}
{/* Radar Conic Sweep Beam */}
{radarSweep && !isClosed && (
<div
aria-hidden="true"
className="absolute inset-0 rounded-full animate-spin pointer-events-none opacity-40"
style={{
animationDuration: config.sweepDuration,
background:
"conic-gradient(from 0deg, transparent 0deg, currentColor 50deg, transparent 55deg)",
}}
/>
)}
{/* Radiating Glow Ping Ring */}
{pulse && !isClosed && (
<span
aria-hidden="true"
className={cn(
"absolute rounded-full bg-current opacity-75 animate-ping",
size === "sm" ? "size-2.5" : size === "md" ? "size-4" : "size-6"
)}
/>
)}
{/* Center LED Core Dot */}
<span
className={cn(
"relative z-10 rounded-full border border-background/60 transition-all",
sizeDot,
config.dotBg,
config.glowClass
)}
>
<span className="absolute top-0.5 left-0.5 size-1 rounded-full bg-white/70" />
</span>
</div>
);
}
export function StatusDot({
level = "low",
variant = "booth",
size = "md",
gateName = "GATE A-04",
terminal = "NORTH TURNSTILE",
laneType = "GENERAL ADMISSION",
occupancyPercent,
waitTime,
throughput,
turnstileId = "TRN-902",
label,
sublabel,
radarSweep = true,
pulse = true,
className,
...props
}: StatusDotProps) {
const config = levelConfigs[level];
const percent = occupancyPercent !== undefined ? occupancyPercent : config.defaultPercent;
const currentWait = waitTime || config.defaultWait;
const currentThroughput = throughput || config.defaultThroughput;
const displayLabel = label || config.label;
const displaySublabel = sublabel || config.sublabel;
// Pure Standalone Dot Variant
if (variant === "dot") {
return (
<div
role="status"
aria-label={`${gateName} status: ${displayLabel}`}
className={cn("inline-flex items-center gap-2", className)}
{...props}
>
<BeaconGraphic
level={level}
size={size}
radarSweep={radarSweep}
pulse={pulse}
/>
{label && (
<span className="font-mono text-xs font-bold uppercase tracking-wider text-foreground">
{label}
</span>
)}
</div>
);
}
// Compact Inline Ticket Badge Variant
if (variant === "badge") {
return (
<div
role="status"
aria-label={`${gateName} status: ${displayLabel}`}
className={cn(
"inline-flex items-center gap-2.5 rounded-xs border-2 border-dashed border-border bg-card px-2.5 py-1.5 font-mono select-none shadow-2xs",
className
)}
{...props}
>
<BeaconGraphic
level={level}
size="sm"
radarSweep={radarSweep}
pulse={pulse}
/>
<div className="flex items-center gap-1.5 text-[10px] font-bold">
<span className="uppercase text-foreground">{gateName}</span>
<span className="text-muted-foreground/60">·</span>
<span
className={cn(
"rounded-xs border px-1.5 py-0.2 uppercase tracking-widest text-[9px]",
config.badgeBorder,
config.badgeBg,
config.textColor
)}
>
{displayLabel}
</span>
</div>
</div>
);
}
// Complete Ticket Booth Card Variant
return (
<div
role="status"
aria-label={`${gateName} admission booth status: ${displayLabel}`}
className={cn(
"relative flex flex-col rounded-sm border-2 border-foreground bg-card p-4 font-mono select-none shadow-md max-w-sm w-full",
className
)}
{...props}
>
{/* Left Perforation Notch Hole */}
<div
aria-hidden="true"
className="pointer-events-none absolute -left-2 top-1/2 -translate-y-1/2 size-3.5 rounded-full border-2 border-foreground bg-background z-10"
/>
{/* Right Perforation Notch Hole */}
<div
aria-hidden="true"
className="pointer-events-none absolute -right-2 top-1/2 -translate-y-1/2 size-3.5 rounded-full border-2 border-foreground bg-background z-10"
/>
{/* Ticket Booth Header */}
<div className="flex items-center justify-between border-b-2 border-dashed border-border pb-2.5">
<div className="flex flex-col">
<span className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
{terminal}
</span>
<span className="text-base font-black tracking-tight text-foreground">
{gateName}
</span>
</div>
<div className="flex flex-col items-end text-right">
<span className="rounded-xs border border-border bg-secondary/80 px-1.5 py-0.5 text-[9px] font-bold uppercase tracking-widest text-muted-foreground">
{turnstileId}
</span>
<span className="mt-1 text-[9px] uppercase tracking-wider text-muted-foreground/80">
{laneType}
</span>
</div>
</div>
{/* Center Radar Scanner Station Screen */}
<div className="my-3 flex items-center gap-4 rounded-xs border border-dashed border-border bg-secondary/30 p-3">
<BeaconGraphic
level={level}
size="lg"
radarSweep={radarSweep}
pulse={pulse}
/>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-2">
<span
className={cn(
"inline-block rounded-xs border px-2 py-0.5 text-[10px] font-bold uppercase tracking-[0.18em]",
config.badgeBorder,
config.badgeBg,
config.textColor
)}
>
{displayLabel}
</span>
<span className="text-xs font-bold text-foreground">
{percent}%
</span>
</div>
<p className="mt-1 text-[10px] text-muted-foreground truncate uppercase tracking-wider">
{displaySublabel}
</p>
{/* Turnstile Occupancy Capacity Meter Bar */}
<div className="mt-2.5">
<div className="relative h-2 w-full overflow-hidden rounded-xs border border-border bg-background">
<div
className={cn("h-full transition-all duration-500", config.meterBg)}
style={{ width: `${Math.min(100, Math.max(0, percent))}%` }}
/>
</div>
<div className="mt-1 flex justify-between text-[8px] uppercase tracking-widest text-muted-foreground">
<span>0% EMPTY</span>
<span>50% FLOW</span>
<span>100% MAX</span>
</div>
</div>
</div>
</div>
{/* Ticket Footer Metrics Grid */}
<div className="grid grid-cols-3 gap-2 border-t-2 border-dashed border-border pt-2.5 text-center">
<div>
<span className="block text-[9px] uppercase tracking-wider text-muted-foreground">
WAIT TIME
</span>
<span className="text-xs font-bold text-foreground">
{currentWait}
</span>
</div>
<div>
<span className="block text-[9px] uppercase tracking-wider text-muted-foreground">
THROUGHPUT
</span>
<span className="text-xs font-bold text-foreground">
{currentThroughput}
</span>
</div>
<div>
<span className="block text-[9px] uppercase tracking-wider text-muted-foreground">
BEACON
</span>
<span className={cn("text-xs font-bold uppercase", config.textColor)}>
{level === "closed" ? "OFFLINE" : "LIVE RADAR"}
</span>
</div>
</div>
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| level? | GateOccupancyLevel | Gate occupancy level |
| variant? | StatusDotVariant | Display variant: complete booth card, inline ticket badge, or pure beacon dot |
| size? | StatusDotSize | Size of the beacon dot |
| gateName? | string | Name or designation of the turnstile gate |
| terminal? | string | Terminal or booth station title |
| laneType? | string | Lane category (e.g. VIP FAST-TRACK, GENERAL ADMISSION) |
| occupancyPercent? | number | Numerical occupancy percentage override (0 to 100) |
| waitTime? | string | Estimated wait duration string |
| throughput? | string | Admission flow rate |
| turnstileId? | string | Turnstile equipment serial or station ID |
| label? | string | Custom label override for the status |
| sublabel? | string | Custom secondary description override |
| radarSweep? | boolean | Whether the radar sweep beam is active |
| pulse? | boolean | Whether glow ring pulses are animated |
| className? | string | Additional CSS class names |