React-based frontend for PaperWords multiplayer word-guessing game with hand-drawn sketch aesthetic.
Project Setup:
- ✅ React 18 with Vite build tool
- ✅ React Router v6 for page navigation
- ✅ Socket.io-client for real-time communication
- ✅ Rough.js integration (ready for Phase 3+ advanced usage)
State Management:
- ✅ SocketContext for global socket access
- ✅ useSocket hook for socket connection management
- ✅ useSession hook for sessionStorage persistence
- ✅ Session restore on tab reload (within 60-second window)
Styling & Design:
- ✅ Mobile-first responsive design (tested 375px - 1920px)
- ✅ Hand-drawn aesthetic with custom CSS and animations
- ✅ Smooth exponential easing (no dated bounce animations)
- ✅ Accessibility-first (keyboard navigation, focus states, color contrast)
- ✅ CSS custom properties for theme management
- ✅ Dark mode support via prefers-color-scheme
Components:
- ✅ Button component (multiple variants: primary, secondary, success, outline, ghost)
- ✅ Input component with validation and error states
- ✅ SocketProvider for context-based socket management
Pages:
- ✅ Home page (create room, join room UI)
- ✅ Room page (waiting for opponent, room state display)
- ✅ NotFound page (404 fallback)
- ✅ Routing via React Router with clean URL structure
Features:
- ✅ Room creation with unique ID generation (backend)
- ✅ Room joining with ID validation
- ✅ Session persistence (sessionStorage)
- ✅ Auto-reconnect on disconnect
- ✅ Error messaging and loading states
- ✅ Responsive mobile-first layout
- ✅ Proper typography hierarchy and spacing rhythm
npm installCopy .env.example to .env:
cp .env.example .envConfigure as needed:
VITE_API_URL: Backend server URL (default: http://localhost:3001)VITE_ENVIRONMENT: development or production
Development mode (with hot reload):
npm run devThe app will start on http://localhost:3000
Production build:
npm run build
npm run previewsrc/
├── index.jsx # Entry point
├── App.jsx # Router setup
├── pages/
│ ├── Home.jsx # Room creation/joining
│ ├── Room.jsx # Room state (Phase 2)
│ └── NotFound.jsx # 404 page
├── components/
│ ├── common/
│ │ ├── Button.jsx # Reusable button
│ │ ├── Input.jsx # Reusable input
│ │ └── Modal.jsx # (Phase 3+)
│ ├── layout/ # (Phase 3+)
│ └── game/ # (Phase 3+)
├── hooks/
│ ├── useSocket.js # Socket.io management
│ └── useSession.js # SessionStorage management
├── context/
│ └── SocketContext.jsx # Global socket provider
├── styles/
│ ├── index.css # Global styles
│ ├── responsive.css # Media queries
│ ├── animations.css # Animation keyframes
│ └── rough.css # Rough.js overrides (Phase 3+)
├── constants/
│ └── gameConstants.js # Game states, timeouts, events
└── utils/
└── api.js # Socket event helpers (Phase 3+)
useSocket:
- Manages Socket.io client connection
- Provides emit, on, off methods
- Tracks connection status, userId, sessionId
- Auto-reconnect with exponential backoff
useSession:
- Manages sessionStorage for session persistence
- Methods: saveSession, updateSession, clearSession, hasValidSession
- Survives tab reload (not browser close)
SocketContext:
- Provides socket instance to entire app via useSocketContext hook
- Prevents prop drilling across component tree
- Primary: #2c3e50 (dark blue-gray)
- Secondary: #e74c3c (coral red)
- Accent: #3498db (sky blue)
- Success: #27ae60 (green)
- Error: #e74c3c (red)
- Background: #faf8f3 (off-white paper)
- Main Font: Segoe UI (clean, readable)
- Sketch Font: Caveat, Cabin Sketch (hand-drawn aesthetic)
- Headings: Hand-drawn font family for character
- Body: Main font for readability
Base unit: 8px (CSS custom properties)
- xs: 0.25rem (2px)
- sm: 0.5rem (4px)
- md: 1rem (8px)
- lg: 1.5rem (12px)
- xl: 2rem (16px)
- 2xl: 3rem (24px)
- Mobile: 320px - 479px
- Tablet: 480px - 767px
- Desktop: 768px - 1199px
- Large Desktop: 1200px+
- Socket connection management
- Room creation and joining
- Session persistence
- Mobile-first responsive design
- Accessibility features
- Word length selection (Host)
- Word submission (both players, hidden)
- Real-time letter guessing
- Position-insensitive feedback display
- Guess history per player
- Win condition detection
- Session restore on reload
- Disconnect/reconnect UI with grace period
- Error handling and user feedback
- Animations for win/loss states
Phase 2 focuses on manual testing:
- Open
http://localhost:3000 - Create a room
- Open another tab, join the room
- Verify Socket.io events fire correctly
- Test on mobile (375px viewport)
- Verify sessionStorage updates
- Test tab reload within 60 seconds
Automated tests coming in Phase 3+ with Vitest and React Testing Library.
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Mobile:
- iOS Safari 14+
- Chrome Android 90+
- ✅ Keyboard navigation (Tab, Enter, Escape)
- ✅ Focus indicators on all interactive elements
- ✅ Color contrast > 4.5:1 for text
- ✅ Semantic HTML (buttons, labels, forms)
- ✅ ARIA attributes where needed
- ✅ Reduced motion support
- ✅ Screen reader friendly
- Initial page load: < 100KB (gzipped)
- Socket.io connection: < 500ms
- React render time: < 50ms
- Vite build time: < 2 seconds (dev mode)
Can't connect to backend:
- Ensure backend is running on port 3001
- Check
VITE_API_URLin.env - Check browser console for CORS errors
Styles not loading:
- Ensure CSS files are imported in index.jsx
- Check browser DevTools for CSS parse errors
- Clear browser cache (Cmd+Shift+R on Mac, Ctrl+Shift+R on Windows)
Socket.io reconnection loops:
- Check backend is healthy:
curl http://localhost:3001/health - Look for CORS origin mismatch in console
- Check browser's Network tab for failed WebSocket upgrades
Mobile touch issues:
- Ensure input font-size is 16px+ (prevents zoom on iOS)
- Test on iOS Safari and Chrome Android separately
- Check viewport meta tag in index.html
- Implement word length selection UI
- Create word submission form (hidden input)
- Build guessing interface with real-time feedback
- Add guess history display
- Implement win/loss detection
- Add animations for letter reveals
MIT
Last Updated: June 16, 2026 | Phase: 2 Complete, 3 Ready