Compact Cartridge
A 2D game-cartridge card: a clip-path cartridge silhouette with layered noisy borders, an inset media well, a label tab, and a vertical Game NFT stamp. From basement.fun.



Usage
import { CompactCartridge } from "~/components/cartridge/compact-cartridge";<CompactCartridgetitle="Ball Blaster"description="Aim, charge, fire. Clear the board."media="/cartridge/samples/ball-blaster.png"topLabel="NEW"accentColor="#f43f5e"backgroundColor="#1a0f14"/>
One shape, stacked four times
The cartridge silhouette is a single clip-path polygon. The trick is layering it: a blurred copy underneath for the drop shadow, a gradient-filled copy for the bevel border, then a slightly-inset copy with the real background on top. A second clip-path carves the media well. Stack them with a 1px offset and the edges read as molded plastic.
A tiled subtle-noise PNG over the fills kills the flat digital look — it's the difference between "div" and "object." The label tab is an inline SVG path tinted with the accent color, and the contrast helper picks black or white text by luminance so the tab stays legible on any accent.
From basement.fun, a project I work on at B3. Game art is placeholder.
Source
download .zipThe full implementation, across 2 files. Copy it in or grab the zip. It leans on a cn() class helper and the theme tokens (--primary, --border, …).
import { useState, type ReactNode } from "react";import { cn } from "~/lib/utils";/** Black or white text for contrast against a hex background. */function contrastText(hex: string): string {const h = hex.replace("#", "");const n =h.length === 3? h.split("").map((c) => parseInt(c + c, 16)): [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));const lum = (0.299 * n[0] + 0.587 * n[1] + 0.114 * n[2]) / 255;return lum > 0.6 ? "rgba(0,0,0,0.7)" : "rgba(255,255,255,0.92)";}export interface CompactCartridgeProps {title: string;description: string;media: string;topLabel?: string;topLabelHoverText?: string;mintText?: string;buttonText?: string;accentColor?: string;backgroundColor?: string;borderStartColor?: string;borderEndColor?: string;shadowColor?: string;onButtonClick?: () => void;button?: ReactNode;className?: string;}/*** A 2D "cartridge" card: a clip-path cartridge silhouette with layered noisy* borders, an inset media well, a label tab, and a vertical "Game NFT" stamp.* Trimmed + dependency-free port from basement.fun.*/export function CompactCartridge({title,description,media,topLabel,topLabelHoverText,mintText = "Mint once, play forever",buttonText = "Play game",accentColor = "#3b82f6",backgroundColor = "#10121a",borderStartColor = "rgba(255,255,255,0.3)",borderEndColor = "rgba(255,255,255,0.1)",shadowColor = "rgba(0,0,0,0.35)",onButtonClick,button,className,}: CompactCartridgeProps) {const [hover, setHover] = useState(false);const accentText = contrastText(accentColor);return (<divclassName={cn("relative w-[372px] max-w-full", className)}style={{ top: topLabel ? 0 : -10 }}>{topLabel && (<divclassName="absolute z-30 w-full cursor-help"onMouseEnter={() => setHover(true)}onMouseLeave={() => setHover(false)}><svg viewBox="-16 -4 320 80" className="w-full"><pathd="m 18 10 v 2 v 15 h 156 v -9 l -13.714 -14.7975 c -1.892 -2.0419 -4.55 -3.2025 -7.335 -3.2025 l -124.951 0 c -5.5228 0 -10 4.4771 -10 10 z"fill={accentColor}/></svg><spanclassName="absolute left-0 top-[12px] z-10 flex w-1/2 justify-center pl-3 font-mono text-xs uppercase"style={{ textShadow: "rgba(0,0,0,0.25) 0 -1px 0" }}><spanclassName="w-[72px] select-none rounded-xl pb-[4px] text-center"style={{backgroundColor: accentColor,color: accentText,boxShadow: `0 0 10px 0 ${accentColor}`,}}>{topLabel}</span></span></div>)}<div className="relative" style={{ aspectRatio: "333 / 229" }}>{/* drop shadow silhouette */}<div className="absolute left-0 top-2 -z-10 h-full w-full blur-md"><divclassName="clip-path-compact-cartridge h-full w-full"style={{ background: shadowColor }}/></div>{/* body: gradient border via clipped stack */}<div className="clip-path-compact-cartridge absolute left-0 top-0 h-full w-full">{/* inner fill */}<divclassName="absolute left-px top-px z-10"style={{ width: "calc(100% - 2px)", height: "calc(100% - 2px)" }}><divclassName="clip-path-compact-cartridge subtle-noise h-full w-full"style={{backgroundColor,boxShadow: "inset 12px 16px 14px 10px rgba(0,0,0,0.3)",}}/></div>{/* outer border gradient */}<divclassName="h-full w-full"style={{background: `linear-gradient(to bottom, ${borderStartColor}, ${borderEndColor})`,}}/>{/* media well */}<divclassName="clip-path-compact-cartridge-media absolute left-0 top-0 z-20 h-[96%] w-[97%]"style={{background:"linear-gradient(to bottom, rgba(255,255,255,0.3), rgba(255,255,255,0.15))",padding: "1px",paddingTop: "2px",}}><divclassName="clip-path-compact-cartridge-media subtle-noise absolute left-px top-px"style={{width: "calc(100% - 2px)",height: "calc(100% - 2px)",backgroundColor,}}><divclassName="absolute left-px top-px"style={{zIndex: 1,width: "calc(100% - 2px)",height: "calc(100% - 2px)",boxShadow:"rgb(0 0 0 / 15%) 18px 11px 20px 22px inset, rgb(0 0 0 / 15%) 18px 50px 36px 22px inset",}}/>{/* content */}<div className="absolute left-[18%] top-[30%] z-20 flex h-[60%] w-[78%] flex-col items-center justify-center gap-4"><div className="flex w-full items-center justify-center gap-3"><div className="size-20 flex-none overflow-hidden rounded-2xl border border-white/15"><img src={media} alt={title} className="size-full object-cover" /></div><div className="flex flex-grow flex-col gap-1"><span className="font-semibold text-white drop-shadow-md">{title}</span><spanclassName={cn("font-medium text-white/80 drop-shadow-md",description.length > 70 ? "text-xs" : "text-sm",)}>{description.length > 80 ? `${description.slice(0, 80)}…` : description}</span></div></div>{button ?? (<buttontype="button"onClick={onButtonClick}className="w-full rounded-xl px-4 py-2 text-sm font-semibold shadow-md transition-[filter] hover:brightness-110 active:translate-y-px"style={{ backgroundColor: accentColor, color: accentText }}>{buttonText}</button>)}</div>{/* vertical stamp */}<spanclassName="absolute bottom-10 left-5 -rotate-90 font-mono font-semibold uppercase drop-shadow-md"style={{fontSize: 10,letterSpacing: 1,color: "rgba(0,0,0,0.5)",textShadow: "rgba(255,255,255,0.1) -1px -1px 0",}}>Game NFT</span></div>{/* mint / hover text */}<spanclassName="absolute right-8 top-8 font-mono font-bold uppercase drop-shadow-md transition-opacity"style={{fontSize: 10,letterSpacing: 1,color: "rgba(0,0,0,0.4)",textShadow: "rgba(255,255,255,0.1) -1px -1px 0",}}>{hover && topLabelHoverText ? topLabelHoverText : mintText}</span></div></div></div></div>);}
/* ── Compact cartridge (2D clip-path card), from basement.fun ───────────── */.subtle-noise { background-image: url("/cartridge/subtle-noise.png"); background-repeat: repeat; }.clip-path-compact-cartridge {clip-path: polygon(17.006% 10.554%,17.006% 10.554%,16.906% 10.164%,16.777% 9.803%,16.62% 9.474%,16.44% 9.179%,16.237% 8.921%,16.016% 8.704%,15.777% 8.53%,15.525% 8.403%,15.261% 8.324%,14.988% 8.297%,12.745% 8.297%,12.745% 8.297%,11.648% 8.433%,10.608% 8.825%,9.637% 9.454%,8.751% 10.297%,7.963% 11.333%,7.288% 12.54%,6.738% 13.898%,6.328% 15.385%,6.071% 16.98%,5.983% 18.661%,5.983% 89.636%,5.983% 89.636%,6.071% 91.317%,6.328% 92.912%,6.738% 94.399%,7.288% 95.757%,7.963% 96.964%,8.751% 98%,9.637% 98.843%,10.608% 99.472%,11.648% 99.864%,12.745% 100%,93.238% 100%,93.238% 100%,94.335% 99.864%,95.375% 99.472%,96.346% 98.843%,97.232% 98%,98.02% 96.964%,98.695% 95.757%,99.245% 94.399%,99.655% 92.912%,99.911% 91.317%,100% 89.636%,100% 18.661%,100% 18.661%,99.911% 16.98%,99.655% 15.385%,99.245% 13.898%,98.695% 12.54%,98.02% 11.333%,97.232% 10.297%,96.346% 9.454%,95.375% 8.825%,94.335% 8.433%,93.238% 8.297%,38.301% 8.297%,38.301% 8.297%,38.028% 8.324%,37.764% 8.403%,37.512% 8.53%,37.273% 8.704%,37.052% 8.921%,36.849% 9.179%,36.669% 9.474%,36.512% 9.803%,36.383% 10.164%,36.283% 10.554%,36.283% 10.554%,36.183% 10.944%,36.054% 11.305%,35.898% 11.635%,35.717% 11.929%,35.515% 12.187%,35.293% 12.404%,35.055% 12.578%,34.803% 12.706%,34.539% 12.784%,34.266% 12.811%,19.023% 12.811%,19.023% 12.811%,18.75% 12.784%,18.486% 12.706%,18.234% 12.578%,17.996% 12.404%,17.774% 12.187%,17.572% 11.929%,17.391% 11.635%,17.235% 11.305%,17.106% 10.944%,17.006% 10.554%,8.72% 19.698%,8.72% 19.698%,8.741% 19.29%,8.803% 18.903%,8.903% 18.542%,9.037% 18.212%,9.201% 17.919%,9.392% 17.667%,9.607% 17.463%,9.843% 17.31%,10.095% 17.215%,10.362% 17.182%,10.362% 17.182%,10.628% 17.215%,10.881% 17.31%,11.116% 17.463%,11.331% 17.667%,11.523% 17.919%,11.687% 18.212%,11.82% 18.542%,11.92% 18.903%,11.982% 19.29%,12.004% 19.698%,12.004% 28.65%,12.004% 28.65%,11.982% 29.058%,11.92% 29.445%,11.82% 29.806%,11.687% 30.136%,11.523% 30.429%,11.331% 30.681%,11.116% 30.885%,10.881% 31.038%,10.628% 31.133%,10.362% 31.166%,10.362% 31.166%,10.095% 31.133%,9.843% 31.038%,9.607% 30.885%,9.392% 30.681%,9.201% 30.429%,9.037% 30.136%,8.903% 29.806%,8.803% 29.445%,8.741% 29.058%,8.72% 28.65%,8.72% 19.698%);}.clip-path-compact-cartridge-media {clip-path: polygon(100% 33.959%,100% 33.959%,99.909% 32.186%,99.646% 30.504%,99.225% 28.936%,98.661% 27.503%,97.967% 26.23%,97.159% 25.137%,96.249% 24.248%,95.254% 23.585%,94.186% 23.171%,93.06% 23.028%,21.852% 23.028%,21.852% 23.028%,20.726% 23.171%,19.659% 23.585%,18.663% 24.248%,17.754% 25.137%,16.945% 26.23%,16.251% 27.503%,15.687% 28.936%,15.266% 30.504%,15.003% 32.186%,14.912% 33.959%,14.912% 40.077%,14.912% 40.077%,14.896% 40.814%,14.849% 41.545%,14.772% 42.266%,14.664% 42.976%,14.526% 43.673%,14.359% 44.353%,14.164% 45.015%,13.94% 45.657%,13.689% 46.275%,13.411% 46.868%,9.934% 53.775%,9.934% 53.775%,9.752% 54.164%,9.587% 54.569%,9.44% 54.99%,9.312% 55.424%,9.203% 55.87%,9.112% 56.327%,9.041% 56.793%,8.99% 57.266%,8.96% 57.745%,8.949% 58.229%,8.949% 92.831%,8.949% 92.831%,9.009% 93.994%,9.181% 95.097%,9.457% 96.126%,9.827% 97.065%,10.282% 97.9%,10.813% 98.617%,11.409% 99.2%,12.062% 99.635%,12.762% 99.906%,13.501% 100%,21.469% 100%,21.469% 100%,21.528% 99.999%,21.587% 99.998%,21.646% 99.995%,21.705% 99.991%,21.763% 99.985%,21.822% 99.979%,21.88% 99.971%,21.938% 99.962%,21.995% 99.953%,22.053% 99.942%,93.06% 99.942%,93.06% 99.942%,94.186% 99.798%,95.254% 99.384%,96.249% 98.721%,97.159% 97.832%,97.967% 96.74%,98.661% 95.466%,99.225% 94.034%,99.646% 92.466%,99.909% 90.784%,100% 89.011%,100% 33.959%);}