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

@freegamestore/games

v0.16.0

Published

Shared React UI primitives for games on FreeGameStore — GameShell, GameTopbar, etc. Brand-consistent, scroll-free.

Readme

@freegamestore/games

Shared React UI primitives for games on freegamestore.online. Brand-consistent layout, scroll-free viewport lock, touch-friendly controls, and synthesized sound effects.

Version: 0.14.0

Installation

npm i @freegamestore/games
# or
pnpm add @freegamestore/games

Peer dependency: React 19+.

Quick Start

import { GameShell, GameTopbar } from '@freegamestore/games'

export default function App() {
  return (
    <GameShell topbar={<GameTopbar title="Chess" score={42} />}>
      {/* your game canvas / DOM */}
    </GameShell>
  )
}

Components

GameShell

Root layout wrapper. Locks the game to 100svh, prevents document scroll, disables text selection and touch callout. Every game wraps its content in this.

<GameShell topbar={<GameTopbar title="Tetris" score={42} />}>
  <MyGame />
</GameShell>

| Prop | Type | Description | |------|------|-------------| | topbar | ReactNode? | Topbar element (typically <GameTopbar />) | | children | ReactNode | Game content — fills remaining viewport |

GameTopbar

The single allowed topbar. Same font, padding, and color tokens across every game.

// Simple: just a score
<GameTopbar title="Tetris" score={42} />

// Custom stats + play/pause controls
<GameTopbar
  title="Pac-Man"
  stats={[
    { label: 'Score', value: 1200, accent: true },
    { label: 'Lives', value: 3 },
    { label: 'Level', value: 5 },
  ]}
  rules={<p>Eat all the dots. Avoid ghosts.</p>}
  onPlayPause={togglePause}
  paused={isPaused}
  onRestart={restart}
/>

| Prop | Type | Description | |------|------|-------------| | title | string? | Game name, left side | | score | number? | Convenience for single-score games | | stats | GameTopbarStat[]? | Custom stat lineup (replaces score) | | actions | ReactNode? | Right-side buttons (max 2) | | rules | ReactNode? | Game instructions — shows info icon, opens overlay on tap | | onPlayPause | () => void? | Renders play/pause icon button | | paused | boolean? | Controls play/pause icon state | | onRestart | () => void? | Renders restart icon button |

GameAuth

Sign-in / avatar widget for the topbar actions slot. Shows "Sign in" when signed out, avatar + dropdown when signed in.

<GameTopbar title="Chess" score={42} actions={<GameAuth />} />

No props — uses useAuth internally.

GameButton

Touch-friendly button. Min 44px touch target. Four variants, three sizes.

<GameButton variant="primary" size="lg" onClick={start}>Play Again</GameButton>
<GameButton variant="secondary" size="sm" onClick={undo}>Undo</GameButton>
<GameButton variant="ghost" size="sm" onClick={flip}>Flip</GameButton>
<GameButton variant="danger" onClick={quit}>Quit</GameButton>

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | (required) | Button content | | variant | 'primary' \| 'secondary' \| 'ghost' \| 'danger' | 'primary' | Visual style | | size | 'sm' \| 'md' \| 'lg' | 'md' | Touch-target size | | onClick | () => void? | | Click handler | | disabled | boolean? | false | Disabled state | | block | boolean? | false | Full width |

Sizes: sm (44px), md (48px), lg (56px).

GameModal

Fullscreen modal overlay. Closes on backdrop click or Escape.

<GameModal open={showSettings} onClose={() => setShowSettings(false)} title="Settings">
  <p>Game settings here</p>
</GameModal>

| Prop | Type | Description | |------|------|-------------| | open | boolean | Whether the modal is visible | | onClose | () => void | Called on backdrop click or Escape | | title | string? | Header text | | children | ReactNode | Modal content |

GameOverScreen

Standard game-over overlay with score, high score, and play again button.

<GameOverScreen
  score={1200}
  highScore={1500}
  onPlayAgain={restart}
>
  <Leaderboard {...leaderboard} />
</GameOverScreen>

| Prop | Type | Description | |------|------|-------------| | score | number | Final score | | highScore | number? | Personal best (shows "New high score!" when beaten) | | onPlayAgain | () => void | Play again handler | | children | ReactNode? | Extra content below the score |

GameConfirm

Confirm/cancel dialog built on GameModal.

<GameConfirm
  open={showQuit}
  title="Quit Game?"
  message="Your progress will be lost."
  onConfirm={quit}
  onCancel={() => setShowQuit(false)}
  variant="danger"
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | open | boolean | | Whether visible | | title | string | | Dialog title | | message | string | | Body text | | onConfirm | () => void | | Confirm handler | | onCancel | () => void | | Cancel handler | | confirmLabel | string? | 'Confirm' | Confirm button text | | cancelLabel | string? | 'Cancel' | Cancel button text | | variant | 'default' \| 'danger' | 'default' | Confirm button style |

GameThemeToggle

Compact sun/moon theme toggle (28px). Cycles system/light/dark. Designed for the topbar actions slot.

<GameTopbar title="Chess" actions={<><GameThemeToggle /><GameTextSizeToggle /></>} />

No props.

GameTextSizeToggle

Compact text-size toggle (28px). Cycles default/large/small. Shows A/A+/A-. Designed for the topbar actions slot.

<GameTopbar title="Chess" actions={<GameTextSizeToggle />} />

No props.

Leaderboard

Tabbed leaderboard display (Top / Recent). Use with the useLeaderboard hook.

const { topScores, recentScores, loading } = useLeaderboard('chess')

<Leaderboard topScores={topScores} recentScores={recentScores} loading={loading} />

| Prop | Type | Description | |------|------|-------------| | topScores | LeaderboardEntry[] | All-time top scores | | recentScores | LeaderboardEntry[] | Recent scores | | loading | boolean | Loading state |

Hooks

useAuth

Auth state for FreeGameStore (Google OAuth via auth.freegamestore.online).

const { user, loading, signIn, signOut } = useAuth()

Returns { user: User | null, loading: boolean, signIn: () => void, signOut: () => void }.

useLeaderboard

Fetch and submit scores to the platform leaderboard.

const { topScores, recentScores, submitScore, loading, refresh } = useLeaderboard('tetris')

// Submit a score
const { ok, rank } = await submitScore(1200)

useGameSounds

Synthesized sound effects via Web Audio API. Zero audio files. All sounds auto-respect the mute toggle.

const { playMove, playScore, playError, playGameOver, playLevelUp, playDrop, playClear, playTick } = useGameSounds()

// Play a sound (no-op when muted)
playScore()

| Sound | Use case | |-------|----------| | playMove | Piece moved, card flipped, button pressed | | playScore | Scored a point, matched, correct answer | | playError | Wrong answer, hit obstacle, lost life | | playGameOver | Game over — descending tones | | playLevelUp | Level up / achievement — ascending arpeggio | | playDrop | Hard drop / thud | | playClear | Line clear / combo | | playTick | Countdown tick / timer warning |

useSound

Low-level mute state from the SDK's SoundProvider. Games should check muted before playing any custom audio.

const { muted, toggle } = useSound()
if (!muted) myCustomAudio.play()

Exports

@freegamestore/games → GameShell, GameTopbar, GameAuth, GameButton, GameModal,
                        GameOverScreen, GameConfirm, GameThemeToggle,
                        GameTextSizeToggle, Leaderboard, useAuth, useLeaderboard,
                        useGameSounds, useSound

License

MIT