npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gagandeep023/stone-age-frontend

v0.6.2

Published

Stone Age board game frontend - React components, animations, and Socket.IO hooks

Readme

@gagandeep023/stone-age-frontend

A complete React frontend for the Stone Age board game, built with TypeScript. Includes all game UI components, Socket.IO multiplayer hooks, SVG board rendering, dice animations, and a pre-built lobby system.

Designed as a standalone npm package that can be dropped into any React application.

Table of Contents

Features

  • Complete Game UI - Lobby, board, player boards, action panels, scoring screen
  • SVG Game Board - Interactive board with 15 clickable locations, worker visualization, and resource tracking
  • Real-time Multiplayer - Socket.IO hooks with automatic reconnection and connection status
  • Room Management - Create, join, and manage game rooms with public/private support
  • 3-Phase UI - Distinct interfaces for worker placement, action resolution, and feeding
  • Dice Animations - 3D rolling animation with tool bonus previews
  • Responsive Design - Dark theme with a Stone Age-inspired color palette
  • TypeScript - Full type safety with exported interfaces for all game state

Installation

npm install @gagandeep023/stone-age-frontend

Peer Dependencies

These must be installed separately in your project:

npm install react react-dom socket.io-client

Compatible with React 18 and React 19.

Quick Start

Drop-in Lobby + Game

The fastest way to get started. The lobby handles room creation and joining, and calls onJoinGame when a game starts:

import { useState } from 'react';
import { StoneAgeLobby, StoneAgeGame } from '@gagandeep023/stone-age-frontend';
import '@gagandeep023/stone-age-frontend/frontend/styles.css';

function App() {
  const [gameId, setGameId] = useState<string | null>(null);

  const user = {
    id: 'user-123',
    name: 'Alice',
    picture: '/avatars/alice.png',
  };

  if (gameId) {
    return (
      <StoneAgeGame
        gameId={gameId}
        wsBase="http://localhost:3001"
        user={user}
        authToken="your-auth-token"
        onLeave={() => setGameId(null)}
      />
    );
  }

  return (
    <StoneAgeLobby
      apiBase="http://localhost:3001"
      wsBase="http://localhost:3001"
      user={user}
      authToken="your-auth-token"
      onJoinGame={(id) => setGameId(id)}
    />
  );
}

Components

StoneAgeLobby

The pre-game lobby for browsing, creating, and joining game rooms.

<StoneAgeLobby
  apiBase="http://localhost:3001"
  wsBase="http://localhost:3001"
  user={{ id: 'user-1', name: 'Alice' }}
  authToken="token"
  onJoinGame={(gameId) => console.log('Joined:', gameId)}
/>

Features:

  • Create rooms (set name, max players 2-4, private/public, optional passcode)
  • Browse and join public rooms
  • Join private rooms by code + passcode
  • Player list with real-time connection status
  • Start game button (host only, requires 2+ players)

Props:

| Prop | Type | Description | |------|------|-------------| | apiBase | string | Backend API base URL | | wsBase | string | WebSocket server base URL | | user | { id, name, picture? } | Current user info | | authToken | string | Auth token for Socket.IO | | onJoinGame | (gameId: string) => void | Called when game starts |


StoneAgeGame

The main game interface. Renders the board, player panels, action controls, and game log.

<StoneAgeGame
  gameId="game-abc-123"
  wsBase="http://localhost:3001"
  user={{ id: 'user-1', name: 'Alice' }}
  authToken="token"
  onLeave={() => navigate('/lobby')}
/>

Layout:

  • Top: HUD showing round number, current phase, active player
  • Left: Interactive SVG game board
  • Right: Action panel (changes per phase), player boards, game log
  • Overlays: Phase change notifications, turn alerts, game over scoring

Props:

| Prop | Type | Description | |------|------|-------------| | gameId | string | ID of the game to join | | wsBase | string | WebSocket server base URL | | user | { id, name, picture? } | Current user info | | authToken | string | Auth token for Socket.IO | | onLeave | () => void | Called when player leaves game |


GameBoard

SVG-based interactive game board with all 15 locations.

Locations displayed:

  • 5 resource gathering areas (Hunting Grounds, Forest, Clay Pit, Quarry, River)
  • 3 village locations (Tool Maker, Hut, Field)
  • 4 building stacks
  • 4 civilization card slots

Visual features:

  • Color-coded locations by type
  • Gold highlight on available locations (clickable)
  • Worker dots colored by player
  • Worker count badges
  • Supply counter display
  • Blocked location indicators

Action Panels

Three phase-specific control panels:

PlacementPanel (Worker Placement phase)

  • Location selection with worker count adjustment
  • Fixed worker count for village locations (1 for Tool Maker/Field, 2 for Hut)
  • Variable count with +/- controls for resource locations

ActionPanel (Action Resolution phase)

  • Dice roller with animated results
  • Tool selection toggles with bonus preview
  • Resource collection confirmation
  • Skip option for building/card locations
  • Unresolved location list

FeedingPanel (Feeding phase)

  • Food needed vs. available display
  • Resource-to-food conversion controls (+/- per resource type)
  • Running total tracker
  • Starvation penalty option (-10 VP)

Supporting Components

| Component | Description | |-----------|-------------| | GameHUD | Top bar showing round, phase, and current player | | PlayerBoard | Player status card (resources, workers, tools, score) | | GameLog | Scrolling event log, color-coded by event type | | FinalScoring | Game over screen with ranked score breakdown | | DiceRoller | Animated dice faces with 3D roll effect | | ResourceIcon | SVG icons for wood, brick, stone, gold, food | | WorkerMeeple | SVG meeple in player colors | | ToolIcon | SVG hammer icon with level indicator |

Hooks

useStoneAgeSocket

Manages the Socket.IO connection and provides all game action emitters.

import { useStoneAgeSocket } from '@gagandeep023/stone-age-frontend';

const {
  connected,
  connectionError,
  createRoom,
  joinRoom,
  leaveRoom,
  startGame,
  placeWorkers,
  resolveAction,
  useTools,
  confirmResourceGathering,
  payForBuilding,
  payForCard,
  skipAction,
  feedWorkers,
  acceptStarvation,
  chooseDiceReward,
  reconnect,
} = useStoneAgeSocket({
  wsBase: 'http://localhost:3001',
  authToken: 'token',
  onGameState: (state) => console.log('Game updated:', state),
  onRoomUpdate: (room) => console.log('Room updated:', room),
  onRoomList: (rooms) => console.log('Rooms:', rooms),
  onError: (msg) => console.error('Error:', msg),
  onGameOver: (scores) => console.log('Game over:', scores),
  onPhaseChange: (phase, round) => console.log('Phase:', phase, round),
  onTurnChange: (playerId) => console.log('Turn:', playerId),
  onPlayerDisconnected: (playerId) => console.log('Disconnected:', playerId),
  onPlayerReconnected: (playerId) => console.log('Reconnected:', playerId),
});

Actions:

| Method | Args | Description | |--------|------|-------------| | createRoom | name, maxPlayers, passcode? | Create a new room | | joinRoom | roomId, passcode? | Join an existing room | | leaveRoom | - | Leave current room | | startGame | - | Start game (host only) | | placeWorkers | location, count | Place workers on board | | resolveAction | location | Resolve a location (triggers dice) | | useTools | toolIndices | Apply tools to dice roll | | confirmResourceGathering | - | Confirm resource collection | | payForBuilding | resources | Pay for building tile | | payForCard | resources | Pay for civilization card | | skipAction | - | Skip optional action | | feedWorkers | resourcesAsFood? | Feed workers | | acceptStarvation | - | Accept -10 VP penalty | | chooseDiceReward | choice | Choose dice-for-items reward | | reconnect | - | Manual reconnection |


useGameState

Manages game state and provides computed values for the current player.

import { useGameState } from '@gagandeep023/stone-age-frontend';

const {
  gameState,
  updateGameState,
  myPlayer,
  isMyTurn,
  currentPlayer,
  availableLocations,
} = useGameState('user-123');

Returns:

| Property | Type | Description | |----------|------|-------------| | gameState | GameState \| null | Current game state | | updateGameState | (state) => void | State setter | | myPlayer | PlayerState \| undefined | Current user's player data | | isMyTurn | boolean | Whether it's the current user's turn | | currentPlayer | PlayerState \| undefined | Active player data | | availableLocations | LocationId[] | Valid placement targets (filters by capacity, rules, etc.) |

Styling

Import the included stylesheet for the complete Stone Age theme:

import '@gagandeep023/stone-age-frontend/frontend/styles.css';

Color Palette

| Token | Color | Usage | |-------|-------|-------| | Primary BG | #1a1207 | Main background | | Secondary BG | #2a1f0e | Cards, panels | | Board BG | #4a3c1f | Game board | | Primary Text | #f0e6d2 | Main text | | Secondary Text | #b8a88a | Muted text | | Accent Gold | #d4a017 | Highlights, buttons | | Wood | #8B6914 | Wood resource | | Brick | #c45a3c | Brick resource | | Stone | #6B6B6B | Stone resource | | Gold | #d4a017 | Gold resource | | Food | #2D5016 | Food resource |

CSS Classes

All classes are prefixed with sa- (Stone Age) to avoid conflicts:

  • sa-btn, sa-btn-primary, sa-btn-danger - Buttons
  • sa-card - Card containers
  • sa-resource-badge - Resource count badges
  • sa-lobby, sa-lobby-* - Lobby-specific styles

Animations

  • sa-dice-roll - 3D dice rotation (0.5s)
  • sa-dice-bounce - Dice landing bounce (0.3s)
  • sa-score-pop - Score reveal pulse (0.4s)
  • sa-fade-in - Element entry (0.3s)
  • sa-pulse - Infinite attention pulse (1.5s)

Package Exports

// Main export - everything
import {
  StoneAgeLobby,
  StoneAgeGame,
  useStoneAgeSocket,
  useGameState,
} from '@gagandeep023/stone-age-frontend';
import type { GameState, PlayerState } from '@gagandeep023/stone-age-frontend';

// Frontend components only
import {
  StoneAgeLobby,
  StoneAgeGame,
  useStoneAgeSocket,
  useGameState,
} from '@gagandeep023/stone-age-frontend/frontend';

// Types only
import type { GameState, PlayerState, LocationId } from '@gagandeep023/stone-age-frontend/types';

// Styles
import '@gagandeep023/stone-age-frontend/frontend/styles.css';

Subpath Exports

| Path | Contents | |------|----------| | . | All components, hooks, and type definitions | | ./frontend | Components and hooks only | | ./frontend/styles.css | Compiled CSS stylesheet | | ./types | TypeScript type definitions only |

Socket.IO Integration

The frontend connects to a Stone Age backend server (see @gagandeep023/stone-age-backend) via Socket.IO. The connection is managed by the useStoneAgeSocket hook.

Connection Flow

  1. Hook initializes Socket.IO connection with auth token
  2. Server sends roomList with available rooms
  3. User creates or joins a room; server sends roomUpdate
  4. Host starts game; server sends initial gameState
  5. Each player action emits an event; server validates and broadcasts updated gameState
  6. Phase/turn changes trigger phaseChange and turnChange events
  7. Game over triggers gameOver with final score breakdown

Reconnection

  • Automatic Socket.IO reconnection on disconnect
  • Server maintains 2-minute grace period for reconnecting players
  • Manual reconnect() method available
  • Connection status shown in UI via connected / connectionError state

Game Flow

┌──────────────┐     createRoom / joinRoom     ┌──────────────┐
│              │ ─────────────────────────────> │              │
│    Lobby     │                                │  Room Wait   │
│              │ <───────────────────────────── │              │
│  Browse/     │         leaveRoom              │  See players │
│  Create/     │                                │  Start game  │
│  Join rooms  │                                │  (host only) │
└──────────────┘                                └──────┬───────┘
                                                       │ startGame
                                                       v
┌──────────────────────────────────────────────────────────────────┐
│                         Game Screen                               │
│                                                                   │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │ Worker Placement │─>│Action Resolution│─>│    Feeding       │  │
│  │                  │  │                 │  │                  │  │
│  │ Click location   │  │ Roll dice       │  │ Convert food     │  │
│  │ Set worker count │  │ Apply tools     │  │ Use resources    │  │
│  │ Place workers    │  │ Collect/skip    │  │ Accept penalty   │  │
│  └─────────────────┘  └─────────────────┘  └────────┬────────┘  │
│           ^                                          │           │
│           └──────────── next round ──────────────────┘           │
│                                                                   │
│  Game ends when card deck is empty or a building stack runs out   │
└──────────────────────────────────────────────────────┬────────────┘
                                                       │
                                                       v
                                              ┌─────────────────┐
                                              │  Final Scoring   │
                                              │                  │
                                              │  Ranked results  │
                                              │  Score breakdown │
                                              │  Back to lobby   │
                                              └─────────────────┘

Customization

Using Individual Components

You can use the hooks independently to build a custom UI:

import { useStoneAgeSocket, useGameState } from '@gagandeep023/stone-age-frontend';

function CustomGame({ gameId, user }) {
  const { gameState, myPlayer, isMyTurn, availableLocations } = useGameState(user.id);

  const socket = useStoneAgeSocket({
    wsBase: 'http://localhost:3001',
    authToken: 'token',
    onGameState: (state) => updateGameState(state),
  });

  // Build your own UI using the state and socket actions
  return (
    <div>
      <p>Round: {gameState?.roundNumber}</p>
      <p>My Score: {myPlayer?.score}</p>
      {isMyTurn && <button onClick={() => socket.placeWorkers('forest', 2)}>Place in Forest</button>}
    </div>
  );
}

Overriding Styles

All CSS uses class selectors (no IDs or elements), so you can override any style:

/* Override the accent color */
.sa-btn-primary {
  background-color: #your-color;
  border-color: #your-color;
}

/* Override the board background */
.sa-board {
  background-color: #your-board-color;
}

Project Structure

src/
├── index.ts                          # Main package exports
├── frontend/
│   └── index.ts                      # Frontend-only exports
├── components/
│   ├── Actions/
│   │   ├── ActionPanel.tsx           # Action resolution phase UI
│   │   ├── FeedingPanel.tsx          # Feeding phase UI
│   │   └── PlacementPanel.tsx        # Worker placement phase UI
│   ├── Board/
│   │   └── GameBoard.tsx             # SVG interactive game board
│   ├── Common/
│   │   ├── ResourceIcon.tsx          # Resource SVG icons + badges
│   │   ├── ToolIcon.tsx              # Tool SVG icon with level
│   │   └── WorkerMeeple.tsx          # Player meeple SVG
│   ├── Dice/
│   │   └── DiceRoller.tsx            # Animated dice faces
│   ├── HUD/
│   │   ├── GameHUD.tsx               # Round/phase/turn status bar
│   │   └── GameLog.tsx               # Scrolling event log
│   ├── PlayerBoard/
│   │   └── PlayerBoard.tsx           # Player resources, tools, score
│   ├── Scoreboard/
│   │   └── FinalScoring.tsx          # End-game score breakdown
│   ├── StoneAgeGame/
│   │   └── StoneAgeGame.tsx          # Main game container
│   └── StoneAgeLobby/
│       └── StoneAgeLobby.tsx         # Lobby + room management
├── hooks/
│   ├── useGameState.ts               # Game state + computed values
│   └── useStoneAgeSocket.ts          # Socket.IO connection + actions
├── styles/
│   └── stone-age.css                 # Complete theme stylesheet
└── types/
    └── index.ts                      # All TypeScript interfaces

Tech Stack

  • Framework: React 18/19
  • Language: TypeScript (ES2022 target, JSX automatic)
  • Networking: Socket.IO Client 4.x
  • Build: tsup (ESM + CommonJS dual output)
  • Testing: Vitest

Related

License

MIT