ScratchCard
ScratchCard is the low-level reveal primitive from @luxy-creative/scratch-card. Use it when the host app owns the layout under the cover or needs a custom reward surface.
Basic card
import { ScratchCard } from "@luxy-creative/scratch-card";
<ScratchCard
outcome={outcome}
cover="foil"
renderReveal={(outcome, state) => <RewardCard outcome={outcome} progress={state.progress} />}
/>;
Backend-resolved card
<ScratchCard
resolveOutcome={(context) => api.reserveReward(context.interactionId)}
cover="premium"
effects={{ particles: { maxParticles: 72 }, shader: "auto" }}
renderPendingReveal={() => <RewardSkeleton />}
renderReveal={(outcome, state) => <RewardCard outcome={outcome} progress={state.progress} />}
onRevealComplete={(outcome) => analytics.track("scratch_revealed", { outcomeId: outcome.id })}
/>;
Core props
| Prop | Purpose |
|---|---|
renderReveal(outcome, state) | Renders reward content under the cover. |
renderPendingReveal() | Renders while an async resolver is pending. |
cover | Accepts matte, foil, premium, a React node, or a render callback. |
scratchZones | Limits cover rendering and progress estimation to normalized play areas. |
revealThreshold | Defaults to 0.62; triggers onRevealThreshold once. |
autoReveal | Enables threshold/time-based cover finish behavior. |
effects | Controls shimmer, particles, sparkle, foil, shader fallback, and quality. |
gestureRelations | Forwards Gesture Handler relation methods for nested scroll/carousel conflict handling. |
Accessibility
<ScratchCard
outcome={outcome}
cover="foil"
accessibility={{
label: "Scratch card. Double tap to reveal your reward.",
hint: "Reveals the promotional coupon.",
screenReaderRevealMode: "press",
}}
renderReveal={renderReward}
/>;
screenReaderRevealMode: "press" keeps the card operable without a scratch gesture.
Gesture conflict controls
Scroll-first offer feed:
const feedPan = useMemo(() => Gesture.Native(), []);
<ScratchCard
outcome={outcome}
gestureRelations={{ requireExternalGestureToFail: feedPan }}
renderReveal={renderReward}
/>;
Carousel where horizontal swipes and scratching can coexist:
<ScratchCard
outcome={outcome}
gestureRelations={{ simultaneousWithExternalGesture: carouselGesture }}
renderReveal={renderReward}
/>;
Keep relation objects stable with useMemo or refs in host app code.
Callbacks
onOutcomeResolved(outcome)onScratchStart()onProgress(progress)onRevealThreshold(outcome)onRevealComplete(outcome)onError(error)
Callbacks should not contain secrets, full customer records, raw coupon tokens, or sensitive eligibility data.