Card
Game-card component with neon-colored icon, tag, title, description, and a five-star rating display. Supports a "coming soon" locked overlay state.
<Card card={{
icon: "๐พ",
tag: "ACTION",
tagVariant: "magenta",
title: "Shadow Run",
description: "A game about light and darkness.",
rating: 4,
}} /><Card card={{ icon: "๐พ", tag: "ACTION", tagVariant: "magenta", title: "Shadow Run", description: "Fast-paced platformer.", rating: 4 }} /><Card card={{ icon: "๐", tag: "PUZZLE", tagVariant: "cyan", title: "Vortex", description: "Mind-bending puzzle game.", rating: 5 }} /><Card card={{ icon: "๐ฐ", tag: "BONUS", tagVariant: "yellow", title: "Coin Rush", description: "Collect every coin.", rating: 3 }} /><Card card={{ icon: "๐", tag: "ARCADE", tagVariant: "green", title: "Top Score", description: "Beat the high score.", rating: 5 }} />import { cn } from '@/lib/utils'
import type { CardItem } from '@/types'
interface CardProps {
card: CardItem
className?: string
}
const TAG_CLASS: Record<string, string> = {
magenta: 'card-tag card-tag--magenta',
cyan: 'card-tag card-tag--cyan',
yellow: 'card-tag card-tag--yellow',
green: 'card-tag card-tag--green',
}
const ICON_CLASS: Record<string, string> = {
magenta: 'card-icon-el card-icon--magenta',
cyan: 'card-icon-el card-icon--cyan',
yellow: 'card-icon-el card-icon--yellow',
green: 'card-icon-el card-icon--green',
}
export function Card({ card, className }: CardProps): React.JSX.Element {
const tagClass = TAG_CLASS[card.tagVariant] ?? 'card-tag card-tag--green'
const iconClass = ICON_CLASS[card.tagVariant] ?? 'card-icon-el card-icon--green'
return (
<div className={cn('retro-game-card', card.locked && 'retro-game-card--locked', className)}>
{card.locked && (
<div className="card-locked-overlay">
<span className="card-locked-label">
COMING SOON
</span>
</div>
)}
{/* Icon */}
<span className={iconClass}>
{card.icon}
</span>
{/* Tag โ solid fill */}
<span className={tagClass}>
{card.tag}
</span>
{/* Title */}
<h3 className="card-title">
{card.title}
</h3>
{/* Description */}
<p className="card-description">
{card.description}
</p>
{/* Stars */}
{card.rating > 0 && (
<div className="card-stars">
{Array.from({ length: 5 }, (_, i) => (
<span
key={i}
className={i < card.rating ? 'card-star card-star--active' : 'card-star card-star--inactive'}
>
โ
</span>
))}
</div>
)}
</div>
)
}
Button
Arcade-styled button with neon green, magenta, cyan, and yellow variants in three sizes. Renders as a Next.js Link when an href is provided.
Badge
Neon-glow badge with magenta, cyan, yellow, and green color variants. Used for category tags and status labels in the arcade aesthetic.
PixelCharacter
SVG pixel-art sprite with three variants: hero (neon green), ghost (magenta), and coin (yellow). Supports a CSS float animation and configurable size.
ScoreBoard
High-scores leaderboard that renders as a table on desktop and as stacked cards on mobile. Highlights ranks 1-3 with distinct neon color classes.
SectionHeader
Section heading block with a numbered label, a two-part heading where the last word is accented in neon, and an optional subtitle. Supports left or center alignment.
StatBlock
Single-stat display with a large pixel-font value in neon cyan and a label below. Wrapped in a pixel-border container for the arcade aesthetic.
Text
Polymorphic text primitive with body, caption, label, and code variants styled for the RETRO dark arcade palette.