Skip to content
react-cards-opening

Open from a button

No grid, no scan, no collection. A button deals one card from a pool, weighted by rarity, and the reveal comes out of the button. This is the shape of a daily reward, a loot drop after a level, or a thank-you after a purchase.

The example below, with a card in place of the button so it has somewhere to land.

The code

daily-card.tsx
import { rollCards, useCardOpening } from "@react-cards-opening/core";
import { CardOpening } from "@react-cards-opening/ui";
import { useRef, useState } from "react";
import "@react-cards-opening/ui/styles.css";

import { pool } from "./cards";

export function DailyCard({ userId, today }: { userId: string; today: string }) {
  const button = useRef<HTMLButtonElement>(null);
  // The same person gets the same card all day, on any device.
  const [cards] = useState(() => rollCards({ pool, count: 1, seed: `${userId}:${today}` }));
  const [opened, setOpened] = useState<string[]>([]);
  const hunt = useCardOpening({ cards, opened, onOpen: (id) => setOpened((now) => [...now, id]) });

  return (
    <>
      <button ref={button} onClick={() => hunt.start()} disabled={hunt.running || hunt.allOpened}>
        {hunt.allOpened ? `You got ${cards[0].name}` : "Open today's card"}
      </button>
      <CardOpening opening={hunt.opening} launch={() => button.current} />
    </>
  );
}

Notes

  • The roll is seeded with the person and the day, so a reload cannot reroll it. For a card that is worth something, roll on your server instead.
  • There is no landing, so the card fades out where it is once it has been read. Add one to send it to a counter or a collection.
  • A press on the card turns it early, a second press sends it away. Nothing here had to say so.

Show the odds

tsx
import { chances } from "@react-cards-opening/core";

const odds = chances(pool);

<ul>
  {pool.map((card) => (
    <li key={card.id}>
      {card.name}: {(odds.get(card.id)! * 100).toFixed(1)}%
    </li>
  ))}
</ul>

Last updated . Read this page as Markdown.