Lantern
Status Pill.
The TurtleDeck status label: a colored dot and a mono uppercase word for online, offline, running and idle. Running pulses.
onlineofflinerunningidle
import { StatusPill } from "@/components/ui/status-pill";
export default function StatusPillDemo() {
return (
<div className="flex flex-wrap items-center gap-x-6 gap-y-3">
<StatusPill tone="online" />
<StatusPill tone="offline" />
<StatusPill tone="running" />
<StatusPill tone="idle" />
</div>
);
}Installation#
With the CLI
$npx lanterncn add status-pillThis also installs the Lantern theme and any Lantern UI components it depends on. The shadcn CLI works directly too: npx shadcn@latest add httptim/lantern-ui/status-pill. See Installation if your project is not set up yet.
Manually
Install the dependencies
npm install class-variance-authorityCopy the source into your project
components/ui/status-pill.tsximport * as React from "react"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const statusPillVariants = cva( "inline-flex w-fit shrink-0 items-center gap-[7px] font-mono text-[10px] leading-none font-semibold tracking-[0.15em] whitespace-nowrap uppercase", { variants: { tone: { online: "text-success", offline: "text-destructive", running: "text-primary", idle: "text-muted-foreground", }, }, defaultVariants: { tone: "idle" }, }, ); const dotTones = { online: "bg-success shadow-[0_0_10px_#9bba8660]", offline: "bg-destructive", running: "animate-lantern-pulse bg-primary shadow-[0_0_10px_#f5a66560] [animation-duration:1.2s]", idle: "bg-input", } as const; /** The TurtleDeck status pill: a dot and a mono uppercase label. */ function StatusPill({ className, tone, children, ...props }: React.ComponentProps<"span"> & VariantProps<typeof statusPillVariants>) { const t = tone ?? "idle"; return ( <span data-slot="status-pill" data-tone={t} className={cn(statusPillVariants({ tone: t }), className)} {...props}> <span aria-hidden="true" data-slot="status-pill-dot" className={cn("size-1.5 shrink-0 rounded-full", dotTones[t])} /> {children ?? t} </span> ); } export { StatusPill, statusPillVariants };Update the import paths to match your project
The source imports
cnfrom@/lib/utils.
Usage#
import { StatusPill } from "@/components/ui/status-pill";<StatusPill tone="running">Running</StatusPill>With no children the pill shows the tone name. Add ml-auto through className to push it to the end of a flex row.
Examples#
In a list row
- RunningMiner 01Strip mine y=-54
- OnlineFarmerWheat field
- IdleBuilderNo job
- OfflineScoutLast seen 2h ago
import { BotIcon } from "lucide-react";
import { StatusPill } from "@/components/ui/status-pill";
const turtles = [
{ name: "Miner 01", job: "Strip mine y=-54", tone: "running" as const, label: "Running" },
{ name: "Farmer", job: "Wheat field", tone: "online" as const, label: "Online" },
{ name: "Builder", job: "No job", tone: "idle" as const, label: "Idle" },
{ name: "Scout", job: "Last seen 2h ago", tone: "offline" as const, label: "Offline" },
];
export default function StatusPillList() {
return (
<ul className="w-full max-w-md divide-y divide-border rounded-lg border bg-card">
{turtles.map((t) => (
<li key={t.name} className="flex items-center gap-3 px-4 py-3">
<BotIcon className="size-4 shrink-0 text-primary" aria-hidden="true" />
<div className="min-w-0">
<div className="truncate text-sm font-medium">{t.name}</div>
<div className="truncate font-mono text-[11px] text-muted-foreground">{t.job}</div>
</div>
<StatusPill tone={t.tone} className="ml-auto">
{t.label}
</StatusPill>
</li>
))}
</ul>
);
}