← Back to Crumbs
MarketingReactTypeScriptTailwind
Game Console Card
A pricing card shaped like a handheld console - hovering flips the screen over to reveal the full feature list, while the D-pad and subscribe button stay put on the control panel underneath.
Live preview
Popular
Studio
$24/mo
For small teams shipping fast.
▸ press for detailsWhat's included
- Unlimited projects
- 5 team seats
- Priority support
- Custom domains
- API access
Code
game-console-card.tsx
"use client";
import * as React from "react";
export interface GameConsoleCardProps {
plan: string;
price: string;
period?: string;
tagline?: string;
features: string[];
ctaLabel?: string;
popular?: boolean;
onSubscribe?: () => void;
className?: string;
}
function CheckIcon() {
return (
<svg
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
strokeWidth="2.4"
strokeLinecap="round"
strokeLinejoin="round"
className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#2e3a26] dark:text-[#eaf2de]"
>
<path d="M4 10.5l3.5 3.5L16 6" />
</svg>
);
}
function DPad() {
const bar = "absolute bg-[#3e4b37] dark:bg-[#1c241a]";
return (
<div className="relative h-9 w-9 shrink-0" aria-hidden="true">
<div className={`${bar} left-1/2 top-0 h-full w-[34%] -translate-x-1/2 rounded-[3px]`} />
<div className={`${bar} left-0 top-1/2 h-[34%] w-full -translate-y-1/2 rounded-[3px]`} />
</div>
);
}
const SHELL = "bg-[#cfe1c6] dark:bg-[#3c4a38]";
const SHELL_TRIM = "border-[#3e4b37] dark:border-[#1c241a]";
const SCREEN = "bg-[#f4f1e1] dark:bg-[#23291f]";
const SCREEN_TEXT = "text-[#2e3a26] dark:text-[#eaf2de]";
export function GameConsoleCard({
plan,
price,
period = "/mo",
tagline,
features,
ctaLabel = "Subscribe",
popular = false,
onSubscribe,
className = "",
}: GameConsoleCardProps) {
return (
<div
className={["group w-[268px] rounded-[30px] border-[3px] p-3.5 shadow-[0_10px_0_rgba(0,0,0,0.12)]", SHELL, SHELL_TRIM, className]
.filter(Boolean)
.join(" ")}
>
<div className="rounded-2xl bg-[#2b2a24] p-2.5">
<div className="relative h-[186px] [perspective:1400px]">
<div className="relative h-full w-full transition-transform duration-700 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)] group-focus-within:[transform:rotateY(180deg)]">
<div
className={`absolute inset-0 flex flex-col items-center justify-center gap-2 rounded-lg p-5 text-center [backface-visibility:hidden] ${SCREEN}`}
>
{popular ? (
<span className="mb-0.5 rounded-sm bg-[#e2694f] px-2 py-0.5 font-mono text-[9px] font-bold uppercase tracking-wide text-[#2b140d]">
Popular
</span>
) : null}
<h3 className={`font-mono text-[13px] font-bold uppercase tracking-wide ${SCREEN_TEXT}`}>
{plan}
</h3>
<div className="flex items-baseline gap-1">
<span className={`font-mono text-[32px] font-bold leading-none ${SCREEN_TEXT}`}>
{price}
</span>
<span className={`font-mono text-xs opacity-60 ${SCREEN_TEXT}`}>{period}</span>
</div>
{tagline ? (
<p className={`max-w-[170px] text-[12px] leading-snug opacity-80 ${SCREEN_TEXT}`}>
{tagline}
</p>
) : null}
<span className={`mt-1 font-mono text-[10px] uppercase tracking-wide opacity-60 ${SCREEN_TEXT}`}>
▸ press for details
</span>
</div>
<div
className={`absolute inset-0 flex flex-col justify-center gap-2 rounded-lg p-5 [backface-visibility:hidden] [transform:rotateY(180deg)] ${SCREEN}`}
>
<h4 className={`mb-1 font-mono text-[11px] font-bold uppercase tracking-wide opacity-70 ${SCREEN_TEXT}`}>
What's included
</h4>
<ul className="space-y-1.5">
{features.map((feature) => (
<li key={feature} className={`flex items-start gap-2 text-[12.5px] ${SCREEN_TEXT}`}>
<CheckIcon />
<span>{feature}</span>
</li>
))}
</ul>
</div>
</div>
</div>
</div>
<div className="mt-3.5 flex items-center justify-between px-1.5">
<DPad />
<button
type="button"
onClick={onSubscribe}
className="rounded-full bg-[#e2694f] px-7 py-2.5 text-[13px] font-bold text-[#2b140d] shadow-[0_3px_0_#a6452f] transition-transform duration-100 hover:brightness-105 active:translate-y-[3px] active:shadow-none"
>
{ctaLabel}
</button>
</div>
</div>
);
}
Usage
usage.tsx
import { GameConsoleCard } from "@/components/game-console-card";
export function Plans() {
return (
<GameConsoleCard
plan="Studio"
price="$24"
tagline="For small teams shipping fast."
popular
features={["Unlimited projects", "5 team seats", "Priority support"]}
onSubscribe={() => {}}
/>
);
}Dependencies
None - just React and Tailwind.
Responsive
Fixed at 268px wide so the 3D flip math and console proportions stay crisp - arrange a few side by side in a flex or grid container and let them wrap on narrow screens. The back of the screen needs hover or keyboard focus to show, so make sure every feature that matters also fits on the front.
Dark mode
Both the plastic shell and the screen switch via Tailwind's dark: variant - a bright sage case would look out of place sitting on a dark page, so the whole console dims to a mossy green rather than just the screen's background and text (warm cream in light mode, dark charcoal in dark mode) changing on its own. The coral action button and D-pad stay put in both themes.