shadimage

CTA Banner

src/components/thegridcn/cta-banner.tsx

"use client";

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

interface CTABannerProps extends React.HTMLAttributes<HTMLDivElement> {
  description?: string;
  primaryAction?: { label: string; onClick?: () => void };
  secondaryAction?: { label: string; onClick?: () => void };
  title: string;
  variant?: "default" | "highlight";
}

export function CTABanner({
  title,
  description,
  primaryAction,
  secondaryAction,
  variant = "default",
  className,
  ...props
}: CTABannerProps) {
  return (
    <div
      data-slot="tron-cta-banner"
      className={cn(
        "group relative overflow-hidden rounded border bg-card/80 px-6 py-8 text-center backdrop-blur-sm",
        variant === "highlight"
          ? "border-primary/50 shadow-[0_0_40px_rgba(var(--primary-rgb,0,180,255),0.08)]"
          : "border-primary/20",
        className
      )}
      {...props}
    >
      {/* Scanline overlay */}
      <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 top border glow */}
      <div className="pointer-events-none absolute top-0 right-0 left-0 h-px">
        <div
          className="h-full w-1/3 bg-gradient-to-r from-transparent via-primary/60 to-transparent"
          style={{ animation: "ctaSweep 4s ease-in-out infinite" }}
        />
      </div>

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

      {/* Content */}
      <div className="relative">
        <h3 className="font-bold font-display text-foreground text-lg uppercase tracking-wider md:text-xl">
          {title}
        </h3>
        {description ? (
          <p className="mx-auto mt-2 max-w-md text-foreground/60 text-sm">
            {description}
          </p>
        ) : null}

        {/* Actions */}
        {primaryAction || secondaryAction ? (
          <div className="mt-5 flex items-center justify-center gap-3">
            {primaryAction ? (
              <button
                type="button"
                onClick={primaryAction.onClick}
                className="rounded border border-primary bg-primary/20 px-5 py-2 font-mono text-[10px] text-primary uppercase tracking-widest shadow-[0_0_12px_rgba(var(--primary-rgb,0,180,255),0.15)] transition-all duration-300 hover:bg-primary/30"
              >
                {primaryAction.label}
              </button>
            ) : null}
            {secondaryAction ? (
              <button
                type="button"
                onClick={secondaryAction.onClick}
                className="rounded border border-primary/30 px-5 py-2 font-mono text-[10px] text-foreground/60 uppercase tracking-widest transition-colors hover:border-primary/50 hover:text-primary"
              >
                {secondaryAction.label}
              </button>
            ) : null}
          </div>
        ) : null}
      </div>

      {/* Corner decorations */}
      <div className="pointer-events-none absolute top-0 left-0 h-5 w-5 border-primary/40 border-t-2 border-l-2" />
      <div className="pointer-events-none absolute top-0 right-0 h-5 w-5 border-primary/40 border-t-2 border-r-2" />
      <div className="pointer-events-none absolute bottom-0 left-0 h-5 w-5 border-primary/40 border-b-2 border-l-2" />
      <div className="pointer-events-none absolute right-0 bottom-0 h-5 w-5 border-primary/40 border-r-2 border-b-2" />
    </div>
  );
}
Where it goes
ctabanner