shadimage
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 · medium
animationmotion

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;

More from satoriui

Browse all components in this library →