WebberUI

Guess Gap Reveal

Guess-the-gap reveal — drag a slider or draw a trend line to guess the answer first, then let go and watch the real data animate in, with a color block marking the gap between "your guess" and the truth alongside the distribution of other readers' guesses.

Loading preview…
npx shadcn@latest add https://webberui.com/r/guess-gap-reveal.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

72
0.9
1
<GuessGapReveal />

Installation

npx shadcn@latest add https://webberui.com/r/guess-gap-reveal.json

Or, once registries are configured in components.json, install it as @webberui/guess-gap-reveal.

Usage

"Guess first, then reveal" is a classic device in data-visual storytelling: making readers commit to a guess and feel the gap with the truth first-hand makes it stick far better. This component offers two input styles, each its own named export.

Guess a value with a slider (GuessGapReveal)

Suited to questions about a single number (a proportion, an amount, a vote count, and so on). Drag the dot or use the arrow keys to pick a guess, and once you press reveal the marker for the real answer surfaces, with a hatched color block marking the gap between the two. Pass crowd and a histogram of other readers' guesses grows in at the reveal.

import { GuessGapReveal } from "@/components/ui/guess-gap-reveal";

<GuessGapReveal
  question="What share of people do you think drink coffee every day?"
  answer={64}
  min={0}
  max={100}
  suffix="%"
  defaultGuess={40}
  crowd={[24, 27, 30, 31, 33, 35, 40, 45]}
  onReveal={({ gap }) => console.log("gap", gap)}
/>

Draw a trend line (GuessTrendReveal)

Suited to questions about a trajectory. Given a known historical segment known and a future segment unknown to be guessed (carrying the real value), the reader drags each handle to draw the trend they expect; at the reveal the real line is drawn with a stroke animation, the area between the two lines is filled with a color block marking the gap, and the mean absolute error is reported.

import { GuessTrendReveal } from "@/components/ui/guess-gap-reveal";

<GuessTrendReveal
  known={[
    { label: "'19", value: 42 },
    { label: "'20", value: 39 },
    { label: "'21", value: 47 },
    { label: "'22", value: 58 },
  ]}
  unknown={[
    { label: "'23", value: 71 },
    { label: "'24", value: 66 },
    { label: "'25", value: 88 },
  ]}
  suffix="k"
/>

GuessGapReveal Props

PropTypeDefaultDescription
questionReact.ReactNodeThe question text, shown at the very top
answernumberThe real answer value
minnumber0Minimum of the scale
maxnumber100Maximum of the scale
stepnumber1Drag and keyboard increment
defaultGuessnumbermidpoint of the scaleInitial guess value in uncontrolled mode
crowdnumber[]Other readers' guesses; when provided, a distribution histogram is shown after the reveal
format(value: number) => stringthousands separators + suffixValue formatting function
suffixstring""Value suffix (such as % or people)
onReveal(r: GuessGapResult) => voidFires at the reveal, returning { guess, answer, gap }
durationnumber0.9Duration of the number roll at the reveal (seconds)

GuessTrendReveal Props

PropTypeDefaultDescription
knownGuessTrendDatum[]Known historical data points; the line starts from this segment
unknownGuessTrendDatum[]The data points to be guessed, carrying the real value as the answer
domain[number, number]derived automatically from the dataY axis range
heightnumber220SVG height (px, including the label strip at the bottom)
format(v: number) => stringthousands separators + suffixValue formatting function
suffixstring""Value suffix
onReveal(r: GuessTrendResult) => voidFires at the reveal, returning { guesses, answers, meanAbsError }
durationnumber1Duration of the real line's stroke drawing (seconds)

GuessTrendDatum is { label: string; value: number }.

How it works

  • Two-stage state machine: before revealed only the guess updates; afterward the input locks and the reveal animation plays. Pressing "guess again / draw again" returns to the guessing stage to start over.
  • Gap color block: the slider version grows a hatched color block between the two markers; the trend version forms a closed polygon out of "the guess line outbound plus the real line inbound" and fills it, showing the over- and under-estimated areas at a glance.
  • Crowd distribution: the slider version's crowd is binned into a histogram that grows bin by bin at the reveal, with the bin containing the real answer darkened, making it easy to compare "where most people guessed" against "where the truth is".
  • Purely neutral colors: the whole component uses only neutral-* and its dark: variants, distinguishing "your guess" from "the truth" through solid versus hollow, solid versus dashed, and light versus dark rather than relying on an extra palette.

Accessibility

  • The guess handle is role="slider" with aria-valuemin/max/now/valuetext; arrow keys are supported for fine adjustment (the slider version also has PageUp/PageDown and Home/End), and in the trend version each handle can be focused independently and adjusted with the up and down keys.
  • The reveal result is announced through a status region with aria-live="polite", so screen reader users also learn the real value and the gap.
  • With "reduce motion" enabled, the number roll, the growing color block, and the line stroke drawing all present instantly, with the functionality and the information completely unchanged.

On this page