Componentticket-stub1.11 KB gzipZero dependencies

Coupon

Discount coupon with scissor cutout lines and promo code copy.

Preview

Live Preview
OFF25%COUPON

FESTIVAL ADMISSION

Valid for all main stage events.

BIGBULL25
EXP: 31 DEC

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add coupon

Or install the full npm package:

npm install bigbullui
import { Coupon } from "@/components/ui/coupon";

<Coupon
  code="BIGBULL25"
  discount="25%"
  title="FESTIVAL PASS"
  expires="31 DEC"
/>

Usage guidance

Use Coupon when a ticket stub surface needs Discount coupon with scissor cutout lines and promo code copy. Keep props minimal and prefer composition with sibling stubs.

Avoid Coupon for long form flows or dense data grids where a dedicated layout block fits better.

Source code

coupon.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

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

export interface CouponProps extends React.HTMLAttributes<HTMLDivElement> {
  code: string;
  discount: string;
  title: string;
  subtitle?: string;
  expires?: string;
  onApply?: (code: string) => void;
  className?: string;
}

export function Coupon({
  code,
  discount,
  title,
  subtitle,
  expires,
  onApply,
  className,
  ...props
}: CouponProps) {
  const [copied, setCopied] = React.useState(false);

  const handleCopy = () => {
    navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
    onApply?.(code);
  };

  return (
    <div
      className={cn(
        "relative flex flex-col sm:flex-row overflow-hidden rounded-lg border-2 border-foreground bg-card shadow-sm select-none",
        className
      )}
      {...props}
    >
      {/* Left Discount Badge Area */}
      <div className="flex flex-col items-center justify-center bg-accent px-6 py-4 text-accent-foreground sm:w-36">
        <span className="font-mono text-xs uppercase tracking-widest opacity-90">OFF</span>
        <span className="font-mono text-3xl font-black tracking-tight">{discount}</span>
        <span className="font-mono text-[9px] uppercase tracking-wider opacity-80">COUPON</span>
      </div>

      {/* Center Perforation Cutout with Scissors */}
      <div className="relative flex sm:flex-col items-center justify-between">
        <div className="size-4 rounded-full bg-background border-2 border-foreground -ms-2 sm:ms-0 sm:-mt-2 shrink-0" />
        <div className="h-0 w-full sm:w-0 sm:h-full border-t-2 sm:border-t-0 sm:border-l-2 border-dashed border-border" />
        <div className="size-4 rounded-full bg-background border-2 border-foreground -me-2 sm:me-0 sm:-mb-2 shrink-0" />
      </div>

      {/* Right Content Area */}
      <div className="flex flex-1 flex-col justify-between p-4 sm:p-5">
        <div>
          <h3 className="font-mono text-sm font-bold uppercase tracking-wide text-foreground">
            {title}
          </h3>
          {subtitle ? (
            <p className="mt-1 text-xs text-muted-foreground">{subtitle}</p>
          ) : null}
        </div>

        <div className="mt-4 flex flex-wrap items-center justify-between gap-3">
          <div className="flex items-center gap-2">
            <span className="rounded-sm border border-dashed border-foreground/40 bg-secondary px-2.5 py-1 font-mono text-xs font-bold tracking-wider text-foreground">
              {code}
            </span>
            <button
              type="button"
              onClick={handleCopy}
              className="cursor-pointer font-mono text-xs text-accent hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
            >
              {copied ? "COPIED!" : "COPY"}
            </button>
          </div>
          {expires ? (
            <span className="font-mono text-[10px] text-muted-foreground uppercase tracking-wider">
              EXP: {expires}
            </span>
          ) : null}
        </div>
      </div>
    </div>
  );
}

Props

PropTypeDescription
codestring
discountstring
titlestring
subtitle?string
expires?string
onApply?(code: string) => void
className?string