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

@zencemarketing/spin-scratch-sdk

v0.1.0-alpha.2

Published

A fully dynamic, configurable Spin & Win wheel and Scratch Card SDK – Vanilla JS & React.

Readme

SpinWheel SDK + ScratchCard SDK

A dynamic, configurable Spin & Win wheel and Scratch Card SDK for Vanilla JS and React (with full TypeScript support).

Package name: @zencemarketing/spin-scratch-sdk


Who is this for?

If you are a client developer, you generally only need to:

  1. Install the package
  2. Render a container (<div id="..." />)
  3. Call SpinWheel.init(...) or ScratchCard.init(...) (Vanilla) or use the React wrappers

Everything else (HTML structure, styles, DOM events, animations) is handled internally by the SDK.

Requirements

  • Browser environment (needs window and document)
  • Node.js >= 16 for building this SDK

If you use SSR frameworks (Next.js, Remix), initialize the widgets only on the client (e.g. inside useEffect).

Features

| Feature | Details | |---|---| | Dynamic segments | 2–N segments – just pass an array | | Fully configurable | 40+ options for SpinWheel, 50+ for ScratchCard | | Spin physics | Configurable duration, spin count, easing | | Spin limit | Restrict number of spins per session | | Win card overlay | Auto-generated coupon code, copy button, configurable redeem CTA | | Confetti | Toggleable confetti with custom colors and count | | Appearance | Customize ring, pointer, button, background, shadows, animations | | Callbacks | onSpinStart, onSpinEnd, onRedeem, onSpinLimitReached | | Runtime updates | updateOptions(), updateSegments(), updatePrize() | | React support | Thin wrapper with ref for imperative control | | Zero dependencies | CSS is auto-injected; fonts loaded from Google Fonts |


Installation

Install from npm (when published)

npm i @zencemarketing/spin-scratch-sdk

Use locally (not published yet)

In your client app package.json:

{
  "dependencies": {
    "@zencemarketing/spin-scratch-sdk": "file:../path-to/ZenceGamification/sdk"
  }
}

Then:

# In the SDK folder
npm i
npm run build

# In the client folder
npm i

Note: Always point file: to the SDK root folder (the one that contains package.json), not dist/.

TypeScript support

This SDK ships TypeScript declarations out of the box — no @types/ package needed.

Two entry points

| Entry | Import path | Use case | |---|---|---| | Main | @zencemarketing/spin-scratch-sdk | Vanilla JS/TS, factory React | | React | @zencemarketing/spin-scratch-sdk/react | Pre-wired React components |

Recommended import for React + TypeScript

import {
  SpinWheelReact,
  ScratchCardReact,
  type SpinWheelReactProps,
  type ScratchCardReactProps,
  type SpinWheelReactHandle,
  type ScratchCardReactHandle,
  type SegmentConfig,
  type ScratchPrize,
} from '@zencemarketing/spin-scratch-sdk/react';

Recommended import for Vanilla TypeScript

import {
  SpinWheel,
  ScratchCard,
  type SpinWheelOptions,
  type ScratchCardOptions,
  type SegmentConfig,
  type ScratchPrize,
} from '@zencemarketing/spin-scratch-sdk';

Key type tips

  • Segment shape: Required fields are label and color (NOT name)
  • React props: Use SpinWheelReactProps / ScratchCardReactProps — these already omit container (the SDK handles it). Do NOT use Omit<SpinWheelOptions, 'container'> manually.
  • Ref handles: Use SpinWheelReactHandle / ScratchCardReactHandle for useRef generic.

Tip: If you hover on SpinWheel.init and you only see options: SpinWheelOptions, that’s normal. To see all available fields:

  • Use IntelliSense inside SpinWheel.init({ ... }) (Ctrl+Space)
  • Or Ctrl/Cmd-click SpinWheelOptions to open the full type

Quick Start (Vanilla JS)

Option A: Use the UMD bundle in plain HTML

When the package is published, you can use a CDN like unpkg:

<div id="my-wheel"></div>

<!-- Uses the "unpkg" entry from package.json -->
<script src="https://unpkg.com/@zencemarketing/spin-scratch-sdk"></script>

<script>
  var SpinWheel = window.SpinWheelSDK.SpinWheel;

  var wheel = SpinWheel.init({
    container: '#my-wheel',
    segments: [
      { label: 'Free Gift',       color: '#55efc4', icon: '🎁', title: 'Free Gift',       worth: '₹299' },
      { label: 'Surprise Reward', color: '#81ecec', icon: '✨', title: 'Surprise Reward', worth: '₹199' },
      { label: 'Better Luck',     color: '#dfe6e9', icon: '🌟', title: 'Better Luck',     worth: '' },
    ],
    headerTitle: 'Spin & Win',
    headerSubtitle: 'Try your luck!',
    onSpinEnd: function (prize, index) {
      console.log('Won:', prize, 'at index', index);
    },
  });

  // Optional: wheel.spin();
</script>

Option B: Import in a bundler app (Vite/Webpack/etc.)

import { SpinWheel } from '@zencemarketing/spin-scratch-sdk';

const wheel = SpinWheel.init({
  container: '#my-wheel',
  segments: [
    { label: 'Prize 1', color: '#55efc4' },
    { label: 'Prize 2', color: '#81ecec' },
  ],
});

Quick Start (React)

There are two ways to use this SDK in React.

Option A (recommended): Import from /react subpath

The simplest way — components are pre-wired, fully typed, and just work:

import React, { useRef } from 'react';
import {
  SpinWheelReact,
  type SpinWheelReactHandle,
  type SegmentConfig,
} from '@zencemarketing/spin-scratch-sdk/react';

export default function App() {
  const ref = useRef<SpinWheelReactHandle>(null);

  const segments: SegmentConfig[] = [
    { label: 'Free Gift', color: '#55efc4', icon: '🎁', title: 'Free Gift', worth: '₹299' },
    { label: 'Surprise',  color: '#81ecec', icon: '✨', title: 'Surprise',  worth: '₹199' },
    { label: 'Better Luck', color: '#dfe6e9', icon: '🌟' },
  ];

  return (
    <SpinWheelReact
      ref={ref}
      headerTitle="Spin & Win"
      segments={segments}
      onSpinEnd={(prize: SegmentConfig, idx: number) => console.log('Won:', prize, idx)}
    />
  );
}

Option B: Factory function (advanced)

If you need more control or want to avoid the /react subpath:

import React, { useRef } from 'react';
import { createSpinWheelReact, type SpinWheelReactHandle } from '@zencemarketing/spin-scratch-sdk';

const SpinWheelReact = createSpinWheelReact(React);

export default function App() {
  const ref = useRef<SpinWheelReactHandle>(null);

  return (
    <SpinWheelReact
      ref={ref}
      headerTitle="Spin & Win"
      segments={[
        { label: 'Free Gift', color: '#55efc4', icon: '🎁', title: 'Free Gift', worth: '₹299' },
        { label: 'Surprise',  color: '#81ecec', icon: '✨', title: 'Surprise',  worth: '₹199' },
      ]}
      onSpinEnd={(prize, idx) => console.log('Won:', prize, idx)}
    />
  );
}

Option B: Use Vanilla API inside useEffect

This gives you full control, but you must clean up on unmount.

import React, { useEffect } from 'react';
import { SpinWheel } from '@zencemarketing/spin-scratch-sdk';

export default function App() {
  useEffect(() => {
    const wheel = SpinWheel.init({
      container: '#spin-wheel-container',
      segments: [
        { label: 'Prize 1', color: '#55efc4' },
        { label: 'Prize 2', color: '#81ecec' },
      ],
    });

    return () => {
      wheel.destroy();
    };
  }, []);

  return <div id="spin-wheel-container" />;
}

Tip (React StrictMode): React may mount/unmount twice in development. Always keep the cleanup (destroy()) to avoid duplicate widgets.


ScratchCard quick start (Vanilla)

import { ScratchCard } from '@zencemarketing/spin-scratch-sdk';

const card = ScratchCard.init({
  container: '#scratch-card-container',
  prize: { name: 'Gift Voucher', icon: '🎁', label: 'Congratulations!' },
  onReveal: (prize) => console.log('Revealed:', prize),
});

ScratchCard quick start (React)

import React, { useRef } from 'react';
import {
  ScratchCardReact,
  type ScratchCardReactHandle,
  type ScratchPrize,
} from '@zencemarketing/spin-scratch-sdk/react';

export default function App() {
  const ref = useRef<ScratchCardReactHandle>(null);

  return (
    <ScratchCardReact
      ref={ref}
      prize={{ name: 'Gift Voucher', icon: '🎁', label: 'Congratulations!' }}
      onReveal={(prize: ScratchPrize) => console.log('Revealed:', prize)}
    />
  );
}

Or using the factory pattern:

import React, { useRef } from 'react';
import { createScratchCardReact, type ScratchCardReactHandle } from '@zencemarketing/spin-scratch-sdk';

const ScratchCardReact = createScratchCardReact(React);

export default function App() {
  const ref = useRef<ScratchCardReactHandle>(null);

  return (
    <ScratchCardReact
      ref={ref}
      prize={{ name: 'Gift Voucher', icon: '🎁', label: 'Congratulations!' }}
      onReveal={(prize) => console.log('Revealed:', prize)}
    />
  );
}

SpinWheel API Reference

SpinWheel.init(options)SpinWheelInstance

Core Options

| Option | Type | Default | Description | |---|---|---|---| | container | string \| HTMLElement | required | CSS selector or DOM element to mount into | | segments | Array<Segment> | required | Array of prize segments (min 2) | | winningIndex | number \| null | null | Pre-determined winning index. Overridden by forceIndex in .spin() |

Spin Behavior

| Option | Type | Default | Description | |---|---|---|---| | spinDuration | number | 5000 | Total spin animation duration (ms) | | minSpins | number | 5 | Minimum full rotations | | maxSpins | number | 8 | Maximum full rotations | | spinLimit | number \| null | null | Max spins allowed (null = unlimited) |

Header UI

| Option | Type | Default | Description | |---|---|---|---| | headerTitle | string \| null | null | Title above the wheel | | headerSubtitle | string \| null | null | Subtitle below the title | | titleColor | string \| null | null | Custom title color (uses theme.goldLight if null) | | subtitleColor | string \| null | null | Custom subtitle color (uses theme.textMuted if null) |

Hub / Center

| Option | Type | Default | Description | |---|---|---|---| | hubLabel | string | 'Spin & win' | Text inside the center hub | | hubIcon | string | '▲' | Icon inside the center hub |

Spin Button

| Option | Type | Default | Description | |---|---|---|---| | showButton | boolean | true | Show the external SPIN! button | | buttonText | string | 'SPIN!' | Button label text |

Wheel Appearance

| Option | Type | Default | Description | |---|---|---|---| | backgroundColor | string \| null | null | Container background color | | backgroundImage | string \| null | null | Background image URL | | ringColor | string \| null | null | Wheel ring color | | ringShadow | boolean | true | Enable ring shadow/glow | | ringAnimation | boolean | true | Enable pulsing ring animation | | pointerColor | string \| null | null | Pointer/arrow color | | buttonColor | string \| null | null | Spin button gradient color | | buttonShadow | boolean | true | Enable button shadow | | buttonAnimation | boolean | true | Enable button hover/active animations |

Win Card

| Option | Type | Default | Description | |---|---|---|---| | showWinCard | boolean | true | Show win card overlay after spin | | generateCode | boolean | true | Auto-generate a random coupon code | | codeLength | number | 9 | Length of auto-generated codes | | redeemUrl | string \| null | null | URL for redeem button | | winCardBrandLabel | string \| null | null | Brand label on win card (uses hubLabel if null) | | winCardWorthLabel | string | 'WORTH' | Label before worth value | | winCardRedeemButtonText | string | 'Redeem Now' | Redeem button text | | winCardRedeemButtonColorTop | string | '#15803d' | Redeem button gradient top color | | winCardRedeemButtonColorBottom | string | '#166534' | Redeem button gradient bottom color | | winCardRedeemButtonTextColor | string | '#ffffff' | Redeem button text color |

Confetti

| Option | Type | Default | Description | |---|---|---|---| | confettiOnWin | boolean | true | Show confetti on win | | confettiColors | string[] \| null | null | Array of confetti colors | | confettiCount | number | 40 | Number of confetti pieces |

Theme

| Option | Type | Default | Description | |---|---|---|---| | theme | object | See below | Color theme overrides |

Callbacks

| Option | Type | Default | Description | |---|---|---|---| | onSpinStart | function | null | (winIndex) => void | | onSpinEnd | function | null | (prize, winIndex) => void | | onRedeem | function | null | (prize) => void | | onSpinLimitReached | function | null | (count) => void |

Segment Object

{
  label: 'Free Gift',       // text on the wheel (required)
  color: '#55efc4',          // segment background color (required)
  icon:  '🎁',               // emoji/icon (optional)
  title: 'Free Gift Voucher', // title on win card (optional, falls back to label)
  worth: '₹299',             // "WORTH ₹299" on win card (optional)
  code:  'FIXEDCODE',        // fixed code – overrides auto-generate (optional)
}

SpinWheel Instance Methods

| Method | Description | |---|---| | .spin(forceIndex?) | Trigger a spin; optionally force which segment wins | | .updateSegments(segments[]) | Replace all segments at runtime and re-render | | .hideWinCard() | Programmatically close the win card overlay | | .updateOptions(opts) | Update configuration options at runtime | | .resetSpinCount(count?) | Reset the spin count (default: 0) | | .destroy() | Remove all SDK-created DOM and clean up |

SpinWheel Instance Getters

| Getter | Type | Description | |---|---|---| | .lastWonPrize | Segment \| null | The last prize that was won | | .lastWonIndex | number | The last won segment index (-1 if none) | | .spinCount | number | Total spins performed | | .remainingSpins | number \| null | Remaining spins (null if unlimited) |

SpinWheel Theme Defaults

{
  gold:      '#e8c547',
  goldLight: '#f5d76e',
  goldDark:  '#c9a227',
  bgDark:    '#0d0d12',
  textMuted: '#9ca3af',
}

ScratchCard API Reference

ScratchCard.init(options)ScratchCardInstance

Core Options

| Option | Type | Default | Description | |---|---|---|---| | container | string \| HTMLElement | required | CSS selector or DOM element | | prize | Prize | required | Prize object: { name, icon, label } |

Header UI

| Option | Type | Default | Description | |---|---|---|---| | headerTitle | string | 'Scratch the Card...' | Header text | | headerTitleColor | string \| null | null | Header title color |

Instruction

| Option | Type | Default | Description | |---|---|---|---| | instruction | string | 'Scratch the card...' | Instruction text | | instructionColor | string \| null | null | Instruction text color |

Scratch Behavior

| Option | Type | Default | Description | |---|---|---|---| | revealThreshold | number | 55 | % scratched to auto-reveal | | brushSize | number | 28 | Scratch brush size in px |

Coin / Eraser Tool

| Option | Type | Default | Description | |---|---|---|---| | coinSize | number | 56 | Coin cursor size in px | | showCoin | boolean | true | Show coin cursor | | coinIcon | string | '$' | Icon on coin | | coinGradientStart | string \| null | null | Coin gradient start color | | coinGradientEnd | string \| null | null | Coin gradient end color |

Card Appearance

| Option | Type | Default | Description | |---|---|---|---| | cardBackground | string \| null | null | Card background CSS | | cardShadow | boolean | true | Enable card shadow | | cardBorderRadius | number | 24 | Card border radius in px |

Scratch Zone

| Option | Type | Default | Description | |---|---|---|---| | scratchZoneBackground | string \| null | null | Scratch zone background | | scratchZoneShadow | boolean | true | Enable zone shadow | | scratchZoneBorderRadius | number | 20 | Zone border radius in px |

Scratch Layer

| Option | Type | Default | Description | |---|---|---|---| | scratchLayerColor | string | 'rgb(150,130,180)' | Scratch layer color | | scratchLayerSparkles | boolean | true | Show sparkles on layer | | scratchLayerSparkleCount | number | 40 | Number of sparkles |

Prize Display

| Option | Type | Default | Description | |---|---|---|---| | prizeTextColor | string \| null | null | Prize label color | | prizeNameColor | string \| null | null | Prize name color | | prizeIconBackground | string \| null | null | Prize icon background |

Gift Icon Hint

| Option | Type | Default | Description | |---|---|---|---| | showGiftIcon | boolean | true | Show gift icon hint | | giftIcon | string | '🎁' | Gift icon emoji | | giftIconBackground | string \| null | null | Gift icon background |

Modal

| Option | Type | Default | Description | |---|---|---|---| | showModal | boolean | true | Show modal after reveal | | modalTitle | string | 'Congratulations!' | Modal title | | modalTitleColor | string \| null | null | Modal title color | | modalButtonText | string | 'Claim your' | Modal button text prefix | | modalButtonColor | string \| null | null | Modal button background | | modalButtonTextColor | string \| null | null | Modal button text color | | modalBackdropBlur | boolean | true | Enable backdrop blur |

Confetti

| Option | Type | Default | Description | |---|---|---|---| | confettiEnabled | boolean | true | Enable confetti | | confettiColors | string[] \| null | null | Confetti colors array | | confettiCount | number | 100 | Number of confetti pieces | | confettiDuration | number | 5500 | Confetti duration in ms |

Animation

| Option | Type | Default | Description | |---|---|---|---| | animationType | string | 'default' | 'default' | 'bounce' | 'none' | | animationDuration | number | 600 | Animation duration in ms |

Theme

| Option | Type | Default | Description | |---|---|---|---| | theme | object | See below | Color theme overrides |

Callbacks

| Option | Type | Default | Description | |---|---|---|---| | onScratchStart | function | null | () => void | | onScratchProgress | function | null | (percent) => void | | onReveal | function | null | (prize) => void | | onClaim | function | null | (prize) => void |

Prize Object

{
  name: 'iPhone 16',        // Prize name (required)
  icon: '📱',                // Emoji/icon (optional)
  label: 'Congratulations!', // Label above prize name (optional)
}

ScratchCard Instance Methods

| Method | Description | |---|---| | .reveal() | Programmatically reveal the prize | | .reset() | Reset the card for replay | | .hideModal() | Hide the win modal | | .showModal() | Show the win modal | | .updatePrize(prize) | Update prize at runtime and reset card | | .updateOptions(opts) | Update configuration at runtime | | .destroy() | Remove all SDK-created DOM and clean up |

ScratchCard Instance Getters

| Getter | Type | Description | |---|---|---| | .scratchPercent | number | Current scratch progress (0-100) | | .hasRevealed | boolean | Whether the prize has been revealed | | .prize | Prize \| null | Current prize object |

ScratchCard Theme Defaults

{
  purpleDark:  '#4a2c6a',
  purpleMid:   '#6b4a8a',
  purpleLight: '#8b6baa',
  gold:        '#d4a84b',
  goldLight:   '#e8c547',
  goldDark:    '#b8923a',
  white:       '#ffffff',
  textDark:    '#2d2d2d',
  textMuted:   '#6b6b6b',
}

React Ref Methods

SpinWheelReact

| Method / Getter | Description | |---|---| | ref.current.spin(forceIndex?) | Trigger a spin | | ref.current.updateSegments(segs) | Replace segments | | ref.current.hideWinCard() | Close win card | | ref.current.updateOptions(opts) | Update config | | ref.current.resetSpinCount(count?) | Reset spin count | | ref.current.spinCount | Current spin count | | ref.current.remainingSpins | Remaining spins | | ref.current.lastWonPrize | Last won prize | | ref.current.lastWonIndex | Last won index | | ref.current.instance | Raw SpinWheelInstance |

ScratchCardReact

| Method / Getter | Description | |---|---| | ref.current.reveal() | Reveal prize | | ref.current.reset() | Reset card | | ref.current.hideModal() | Hide modal | | ref.current.showModal() | Show modal | | ref.current.updatePrize(prize) | Update prize | | ref.current.updateOptions(opts) | Update config | | ref.current.scratchPercent | Scratch progress | | ref.current.hasRevealed | Is revealed? | | ref.current.prize | Current prize | | ref.current.instance | Raw ScratchCardInstance |


File Structure

sdk/
├── src/
│   ├── SpinWheel.js           ← SpinWheel Core SDK (vanilla JS)
│   ├── SpinWheelReact.js      ← SpinWheel React wrapper (factory)
│   ├── ScratchCard.js         ← ScratchCard Core SDK (vanilla JS)
│   ├── ScratchCardReact.js    ← ScratchCard React wrapper (factory)
│   ├── styles.js              ← SpinWheel styles
│   ├── scratchStyles.js       ← ScratchCard styles
│   ├── index.js               ← Main barrel export
│   ├── react.js               ← React subpath entry (pre-wires factories)
│   └── utils.js               ← Shared utilities
├── dist/                       ← Built SDK files
│   ├── spin-wheel-sdk.umd.js      ← UMD (browser <script>)
│   ├── spin-wheel-sdk.umd.min.js  ← UMD minified
│   ├── spin-wheel-sdk.esm.mjs     ← ESM (import/export)
│   ├── spin-wheel-sdk.cjs.js      ← CommonJS (require)
│   ├── spin-wheel-sdk.d.ts        ← TypeScript declarations (main)
│   ├── react.esm.mjs              ← React subpath ESM
│   ├── react.cjs.js               ← React subpath CJS
│   └── react.d.ts                 ← TypeScript declarations (React)
├── types/                      ← Source TypeScript declarations (copied into dist/)
│   ├── spin-wheel-sdk.d.ts
│   └── react.d.ts
├── scripts/                    ← Build helpers
│   └── copy-types.mjs
├── demo-vanilla-combined.html  ← Combined Vanilla JS demo
├── demo-react-combined.html    ← Combined React demo
├── package.json
├── CHANGELOG.md
├── LICENSE
└── README.md

License

MIT