---
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 (
{children}
);
}
```
### 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 (
);
}
```
## 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