shadimage
thegridcn
componentannouncement-barsource: ok · refreshed

Announcement Bar

npx shadcn add "https://thegridcn.com/r/announcement-bar.json"
free

Where it goes

banner

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Files1 · source cached 10/8/2026, 3:05:35 AM

src/components/thegridcn/announcement-bar.tsx
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

interface AnnouncementBarProps extends React.HTMLAttributes<HTMLDivElement> {
  action?: { label: string; onClick?: () => void };
  dismissible?: boolean;
  icon?: React.ReactNode;
  text: string;
  variant?: "default" | "highlight" | "warning";
}

export function AnnouncementBar({
  text,
  action,
  variant = "default",
  dismissible = true,
  icon,
  className,
  ...props
}: AnnouncementBarProps) {
  const [dismissed, setDismissed] = React.useState(false);

  if (dismissed) {
    return null;
  }

  return (
    <div
      data-slot="tron-announcement-bar"
      className={cn(
        "relative flex items-center justify-center gap-3 overflow-hidden px-4 py-2 font-mono text-xs",
        variant === "default" &&
          "border-primary/30 border-b bg-primary/5 text-foreground/80",
        variant === "highlight" &&
          "border-primary/50 border-b bg-primary/10 text-primary",
        variant === "warning" &&
          "border-amber-500/30 border-b bg-amber-500/5 text-amber-400/90",
        className
      )}
      {...props}
    >
      {/* Scanline */}
      <div className="pointer-events-none absolute inset-0 bg-[repeating-linear-gradient(0deg,transparent,transparent_2px,rgba(0,0,0,0.03)_2px,rgba(0,0,0,0.03)_4px)]" />

      {/* Animated border glow */}
      <div className="pointer-events-none absolute right-0 bottom-0 left-0 h-px">
        <div
          className={cn(
            "h-full w-1/4 bg-gradient-to-r from-transparent to-transparent",
            variant === "warning" ? "via-amber-500/40" : "via-primary/40"
          )}
          style={{ animation: "announceSweep 5s ease-in-out infinite" }}
        />
      </div>

      <style jsx>{`
        @keyframes announceSweep {
          0%, 100% { margin-left: -10%; }
          50% { margin-left: 86%; }
        }
      `}</style>

      <div className="relative flex items-center justify-center gap-3">
        {icon ? <span className="shrink-0">{icon}</span> : null}
        <span className="text-center tracking-wide">{text}</span>
        {action ? (
          <button
            type="button"
            onClick={action.onClick}
            className={cn(
              "shrink-0 rounded border px-2.5 py-0.5 text-[10px] uppercase tracking-widest transition-colors",
              variant === "warning"
                ? "border-amber-500/40 text-amber-400 hover:bg-amber-500/10"
                : "border-primary/40 text-primary hover:bg-primary/10"
            )}
          >
            {action.label}
          </button>
        ) : null}
      </div>

      {dismissible ? (
        <button
          type="button"
          onClick={() => setDismissed(true)}
          className="absolute top-1/2 right-3 -translate-y-1/2 text-foreground/30 transition-colors hover:text-foreground/60"
        >
          <svg
            aria-hidden="true"
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
          >
            <path
              d="M3 3l6 6M9 3l-6 6"
              stroke="currentColor"
              strokeWidth="1.5"
              strokeLinecap="round"
            />
          </svg>
        </button>
      ) : null}
    </div>
  );
}

More from thegridcn

Browse all components in this library →