shadimage

Pricing Card

src/components/thegridcn/pricing-card.tsx

"use client";

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

interface PricingFeature {
  included: boolean;
  text: string;
}

interface PricingCardProps extends React.HTMLAttributes<HTMLDivElement> {
  badge?: string;
  ctaText?: string;
  description?: string;
  features: PricingFeature[];
  highlighted?: boolean;
  onCtaClick?: () => void;
  period?: string;
  price: string;
  title: string;
}

export function PricingCard({
  title,
  price,
  period = "/mo",
  description,
  features,
  ctaText = "SELECT PLAN",
  onCtaClick,
  highlighted = false,
  badge,
  className,
  ...props
}: PricingCardProps) {
  return (
    <div
      data-slot="tron-pricing-card"
      className={cn(
        "group relative flex flex-col overflow-hidden rounded border bg-card/80 backdrop-blur-sm transition-all duration-300",
        highlighted
          ? "border-primary/60 shadow-[0_0_30px_rgba(var(--primary-rgb,0,180,255),0.1)]"
          : "border-primary/20 hover:border-primary/40",
        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)]" />

      {/* Badge */}
      {badge ? (
        <div className="absolute top-3 right-3 rounded border border-primary/50 bg-primary/15 px-2 py-0.5 font-mono text-[9px] text-primary uppercase tracking-widest">
          {badge}
        </div>
      ) : null}

      {/* Header */}
      <div
        className={cn(
          "border-b px-5 pt-5 pb-4",
          highlighted ? "border-primary/30" : "border-border/50"
        )}
      >
        <div className="text-[10px] text-foreground/60 uppercase tracking-widest">
          {title}
        </div>
        <div className="mt-2 flex items-baseline gap-1">
          <span className="font-bold font-mono text-3xl text-foreground">
            {price}
          </span>
          <span className="font-mono text-foreground/40 text-sm">{period}</span>
        </div>
        {description ? (
          <p className="mt-1.5 text-foreground/50 text-xs">{description}</p>
        ) : null}
      </div>

      {/* Features */}
      <div className="flex-1 space-y-2.5 px-5 py-4">
        {features.map((feature, i) => (
          <div key={i} className="flex items-start gap-2 text-xs">
            <span
              className={cn(
                "mt-0.5 shrink-0 font-mono text-[10px]",
                feature.included ? "text-green-500" : "text-foreground/20"
              )}
            >
              {feature.included ? "✓" : "—"}
            </span>
            <span
              className={cn(
                feature.included ? "text-foreground/80" : "text-foreground/30"
              )}
            >
              {feature.text}
            </span>
          </div>
        ))}
      </div>

      {/* CTA */}
      <div className="px-5 pb-5">
        <button
          type="button"
          onClick={onCtaClick}
          className={cn(
            "w-full rounded border py-2 font-mono text-[10px] uppercase tracking-widest transition-all duration-300",
            highlighted
              ? "border-primary bg-primary/20 text-primary shadow-[0_0_12px_rgba(var(--primary-rgb,0,180,255),0.15)] hover:bg-primary/30"
              : "border-primary/30 text-foreground/60 hover:border-primary/50 hover:text-primary"
          )}
        >
          {ctaText}
        </button>
      </div>

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