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 DECInstallation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add couponOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { 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.tsxZero 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
| Prop | Type | Description |
|---|---|---|
| code | string | |
| discount | string | |
| title | string | |
| subtitle? | string | |
| expires? | string | |
| onApply? | (code: string) => void | |
| className? | string |