satoriui
componentbeacon-badgesource: ok · refreshed
Beacon Badge
A status badge with a customizable beacon (ping) animation and blur reveal text.
npx shadcn add "https://satoriui.site/r/beacon-badge.json"free
Where it goes
stats
Visual style
glass-blur
Interaction
animated-counterscroll-reveal
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumanimationmotion
Files1 · source cached 10/8/2026, 4:43:38 AM
packages/beacon-badge/beacon-badge.tsx
"use client";
import { motion } from "motion/react";
export type BeaconColor =
| "green"
| "blue"
| "purple"
| "yellow"
| "pink"
| "orange"
| "red"
| "white"
| "black";
export type RevealDirection = "up" | "down" | "left" | "right" | "none";
export type BorderSize = "sm" | "md" | "lg" | "xl" | "full";
export interface BeaconBadgeProps {
label?: string;
className?: string;
beconColor?: BeaconColor;
beconPulses?: boolean;
speedReveal?: number;
revealDirection?: RevealDirection;
staticTheme?: "dark" | "light";
border?: BorderSize;
}
const colorMap: Record<BeaconColor, { light: string; dark: string }> = {
green: { light: "bg-emerald-500", dark: "bg-emerald-400" },
blue: { light: "bg-sky-500", dark: "bg-sky-400" },
purple: { light: "bg-purple-500", dark: "bg-purple-400" },
yellow: { light: "bg-yellow-500", dark: "bg-yellow-400" },
pink: { light: "bg-pink-500", dark: "bg-pink-400" },
orange: { light: "bg-orange-500", dark: "bg-orange-400" },
red: { light: "bg-red-500", dark: "bg-red-400" },
white: { light: "bg-zinc-200", dark: "bg-white" },
black: { light: "bg-black", dark: "bg-zinc-800" },
};
const borderMap: Record<BorderSize, string> = {
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
xl: "rounded-xl",
full: "rounded-full",
};
const directionMap: Record<
RevealDirection,
{ initial: { x: number; y: number }; animate: { x: number; y: number } }
> = {
up: { initial: { x: 0, y: 20 }, animate: { x: 0, y: 0 } },
down: { initial: { x: 0, y: -20 }, animate: { x: 0, y: 0 } },
left: { initial: { x: 20, y: 0 }, animate: { x: 0, y: 0 } },
right: { initial: { x: -20, y: 0 }, animate: { x: 0, y: 0 } },
none: { initial: { x: 0, y: 0 }, animate: { x: 0, y: 0 } },
};
const BeaconBadge = ({
label = "50+ Components Available",
className,
beconColor = "blue",
beconPulses = true,
speedReveal = 3,
revealDirection = "up",
staticTheme,
border = "full",
}: BeaconBadgeProps) => {
const colorClass = colorMap[beconColor] || colorMap.blue;
const { initial, animate } = directionMap[revealDirection];
const themeClasses = {
container:
staticTheme === "dark"
? "border-white/[0.08] bg-white/[0.03]"
: staticTheme === "light"
? "border-black/[0.08] bg-black/[0.03]"
: "border-black/[0.08] bg-black/[0.03] dark:border-white/[0.08] dark:bg-white/[0.03]",
text:
staticTheme === "dark"
? "text-white/60"
: staticTheme === "light"
? "text-black/60"
: "text-black/60 dark:text-white/60",
beacon:
staticTheme === "dark"
? colorClass.dark
: staticTheme === "light"
? colorClass.light
: `${colorClass.light} dark:${colorClass.dark}`,
};
return (
<motion.div
initial={{ opacity: 0, ...initial }}
animate={{ opacity: 1, ...animate }}
transition={{
duration: 0.5,
ease: "easeOut",
}}
className={`inline-flex items-center gap-2 px-3 py-1.5 border backdrop-blur-sm ${borderMap[border]} ${themeClasses.container} ${className || ""}`}
>
<span className="relative flex h-1.5 w-1.5 shrink-0">
{beconPulses && (
<span
className={`animate-ping absolute inline-flex h-full w-full rounded-full ${themeClasses.beacon} opacity-75`}
/>
)}
<span
className={`relative inline-flex rounded-full h-1.5 w-1.5 ${themeClasses.beacon}`}
/>
</span>
<motion.span
variants={{
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.03 / speedReveal,
delayChildren: 0.1,
},
},
}}
initial="hidden"
animate="visible"
className={`text-[11px] tracking-widest uppercase font-medium ${themeClasses.text}`}
>
{label.split("").map((char, index) => (
<motion.span
key={index}
variants={{
hidden: { opacity: 0, filter: "blur(10px)", y: 5 },
visible: {
opacity: 1,
filter: "blur(0px)",
y: 0,
transition: {
duration: 0.4,
},
},
}}
className="inline-block"
>
{char === " " ? "\u00A0" : char}
</motion.span>
))}
</motion.span>
</motion.div>
);
};
export default BeaconBadge;