--- name: react-game-ui description: React game UI patterns using shadcn/ui, Tailwind, and Framer Motion for polished game interfaces. Use when building HUDs, resource bars, scoreboards, modals, tooltips, card components, or any game UI. Includes micro-interactions, animations, responsive layouts, and accessibility for games. Triggers on requests for game interface components, UI animations, or shadcn/ui game patterns. --- # React Game UI Production-ready UI patterns for game interfaces using shadcn/ui, Tailwind, and Framer Motion. ## Core Components ### Resource Bar ```tsx import { cn } from '@/lib/utils'; import { motion, AnimatePresence } from 'framer-motion'; interface ResourceBarProps { icon: React.ReactNode; value: number; maxValue?: number; label: string; color?: 'gold' | 'green' | 'blue' | 'red'; } const colorMap = { gold: 'from-amber-400 to-yellow-500', green: 'from-emerald-400 to-green-500', blue: 'from-sky-400 to-blue-500', red: 'from-rose-400 to-red-500', }; export function ResourceBar({ icon, value, maxValue, label, color = 'gold' }: ResourceBarProps) { return (
{icon}
{label} {value.toLocaleString()} {maxValue && /{maxValue}}
); } ``` ### Animated Counter ```tsx import { useEffect, useRef } from 'react'; import { motion, useSpring, useTransform } from 'framer-motion'; interface AnimatedCounterProps { value: number; duration?: number; className?: string; } export function AnimatedCounter({ value, duration = 0.5, className }: AnimatedCounterProps) { const spring = useSpring(0, { duration: duration * 1000 }); const display = useTransform(spring, (v) => Math.floor(v).toLocaleString()); useEffect(() => { spring.set(value); }, [spring, value]); return {display}; } ``` ### Meter/Progress Component ```tsx interface MeterProps { value: number; // 0-100 label: string; tier?: 'mini' | 'major' | 'grand'; showValue?: boolean; } const tierStyles = { mini: 'h-2', major: 'h-3', grand: 'h-4', }; export function Meter({ value, label, tier = 'mini', showValue = true }: MeterProps) { const clampedValue = Math.max(0, Math.min(100, value)); return (
{label} {showValue && ( {clampedValue}% )}
= 50 ? 'bg-gradient-to-r from-emerald-500 to-green-400' : 'bg-gradient-to-r from-amber-500 to-orange-400' )} initial={{ width: 0 }} animate={{ width: `${clampedValue}%` }} transition={{ type: 'spring', stiffness: 100, damping: 15 }} />
); } ``` ### Game Card Component ```tsx import { motion } from 'framer-motion'; interface GameCardProps { suit: 'hearts' | 'diamonds' | 'clubs' | 'spades'; rank: string; faceDown?: boolean; onClick?: () => void; } export function GameCard({ suit, rank, faceDown = false, onClick }: GameCardProps) { const isRed = suit === 'hearts' || suit === 'diamonds'; return ( {/* Front */}
{rank} {suitSymbol(suit)}
{/* Back */}
); } function suitSymbol(suit: string) { const symbols = { hearts: '♥', diamonds: '♦', clubs: '♣', spades: '♠' }; return symbols[suit] || ''; } ``` ## Micro-Interactions ### Button with Feedback ```tsx import { Button } from '@/components/ui/button'; import { motion } from 'framer-motion'; export function GameButton({ children, onClick, variant = 'default', ...props }: React.ComponentProps) { return ( ); } ``` ### Coin Pop Animation ```tsx import { motion, AnimatePresence } from 'framer-motion'; interface CoinPopProps { amount: number; position: { x: number; y: number }; onComplete: () => void; } export function CoinPop({ amount, position, onComplete }: CoinPopProps) { return ( +{amount} 🪙 ); } ``` ### Shake on Error ```tsx const shakeAnimation = { x: [0, -10, 10, -10, 10, 0], transition: { duration: 0.4 } }; export function ShakeOnError({ error, children }) { return ( {children} ); } ``` ## Layout Patterns ### Game HUD Layout ```tsx export function GameLayout({ children }: { children: React.ReactNode }) { return (
{/* Top Bar */}
} value={resources.tulipBulbs} label="Tulips" color="green" />
{/* Main Content */}
{children}
{/* Bottom Bar */}
{/* Side Panel */}
); } ``` ### Responsive Game Board ```tsx export function GameBoard({ children }: { children: React.ReactNode }) { return (
{children}
); } ``` ## Modal & Dialog Patterns ### Game Modal with shadcn ```tsx import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { motion, AnimatePresence } from 'framer-motion'; export function GameModal({ open, onOpenChange, title, children }) { return ( {title} {children} ); } ``` ### Tooltip for Game Elements ```tsx import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; export function GameTooltip({ children, content }) { return ( {children} {content} ); } ``` ## Accessibility for Games ### Skip Animation Preference ```tsx import { useReducedMotion } from 'framer-motion'; export function AnimatedElement({ children }) { const shouldReduceMotion = useReducedMotion(); return ( {children} ); } ``` ### Screen Reader Announcements ```tsx import { useEffect, useRef } from 'react'; export function useAnnounce() { const ref = useRef(null); const announce = (message: string) => { if (ref.current) { ref.current.textContent = message; } }; return { announce, AnnouncerRegion: () => (
)}; } // Usage const { announce, AnnouncerRegion } = useAnnounce(); announce('You won 500 coins!'); ``` ## Performance Tips 1. **Memoize heavy components**: `React.memo()` for hex cells, cards 2. **Use CSS transforms**: GPU-accelerated vs layout-triggering properties 3. **Virtualize large lists**: Only render visible items 4. **Debounce rapid updates**: Score counters, resource bars 5. **Lazy load modals**: Don't mount until needed