Skip to content

Guess The Number

A number guessing game where players narrow down a target between 1 and 100 using a slider. Demonstrates reactive state with wcp-ui-state, derived values, and persistent storage for tracking a best score across sessions.

This example is a stress test. The binding expressions here are intentionally complex to push the limits of the expression parser — this is not representative of recommended usage. In practice, keep logic inside bind-event, bind-show, derive, and similar attributes simple. Move anything non-trivial (conditionals, multi-step mutations, derived calculations) into a <script> or a component method instead.

Guess My Number 1 to 100
Set the slider, then press Guess Higher — you guessed Lower — you guessed
tries
remaining
best

Code

The following example assumes that you have imported the component and set up the theme.

Guess The Number
<div class="guess-number-game">
<wcp-ui-state id="guess-number-game-state">
<wcp-store type="number" name="$gameSeed" value="0"></wcp-store>
<wcp-store type="number" name="$currentGuessInput" value="50"></wcp-store>
<wcp-store type="number" name="$lastGuess" value="0"></wcp-store>
<wcp-store type="number" name="$attemptCount" value="0"></wcp-store>
<wcp-store type="number" name="$minimumPossibleValue" value="1"></wcp-store>
<wcp-store type="number" name="$maximumPossibleValue" value="100"></wcp-store>
<wcp-store type="number" name="$bestScore" persistent value="0"></wcp-store>
<wcp-store derive="(Date.now() % 100000 + $gameSeed * 37) % 100 + 1" name="$targetNumber"></wcp-store>
<wcp-store derive="$lastGuess > 0 && $lastGuess === $targetNumber" name="$hasWon"></wcp-store>
<div class="guess-number-game-header">
<span class="guess-number-game-title">Guess My Number</span>
<span class="guess-number-game-subtitle">1 to 100</span>
</div>
<div
class="guess-number-game-feedback"
bind-class="'guess-number-game-feedback-' + ($attemptCount === 0 ? 'idle' : ($hasWon ? 'success' : ($lastGuess < $targetNumber ? 'higher' : 'lower')))"
>
13 collapsed lines
<span bind-show="$attemptCount === 0">Set the slider, then press Guess</span>
<span bind-show="$attemptCount > 0 && !$hasWon && $lastGuess < $targetNumber">
Higher — you guessed
<b bind-text="$lastGuess"></b>
</span>
<span bind-show="$attemptCount > 0 && !$hasWon && $lastGuess > $targetNumber">
Lower — you guessed
<b bind-text="$lastGuess"></b>
</span>
<span bind-show="$hasWon" bind-text="'Got it in ' + $attemptCount + ($attemptCount === 1 ? ' try' : ' tries') + '!'"></span>
</div>
<div class="guess-number-game-picker">
26 collapsed lines
<span class="guess-number-game-current-value" bind-text="$currentGuessInput"></span>
<input
type="range"
class="guess-number-game-slider"
aria-label="Pick a number"
bind-attr="min:$minimumPossibleValue; max:$maximumPossibleValue"
bind-attr-toggle="disabled:$hasWon"
bind-event="input:$currentGuessInput(Number(EVENT.target.value))"
bind-prop="value:$currentGuessInput"
/>
<div
class="guess-number-game-range-bar"
bind-style="
--range-start:($minimumPossibleValue - 1) / 99 * 100;
--range-end:($maximumPossibleValue - 1) / 99 * 100
"
>
<div class="guess-number-game-range-bar-fill"></div>
</div>
<div class="guess-number-game-range-labels">
<span bind-text="$minimumPossibleValue"></span>
<span bind-text="$maximumPossibleValue"></span>
</div>
</div>
<div class="guess-number-game-actions">
36 collapsed lines
<button
class="guess-number-game-guess-button"
bind-attr-toggle="disabled:$hasWon"
bind-event="
click:
$lastGuess($currentGuessInput),
$attemptCount($attemptCount + 1),
$lastGuess < $targetNumber
? $minimumPossibleValue($lastGuess + 1)
: (
$lastGuess > $targetNumber
? $maximumPossibleValue($lastGuess - 1)
: $bestScore($bestScore === 0 || $attemptCount < $bestScore ? $attemptCount: $bestScore)
),
$lastGuess < $targetNumber
? $currentGuessInput($lastGuess + 1)
: ($lastGuess > $targetNumber ? $currentGuessInput($lastGuess - 1) : null)
"
>
Guess
</button>
<button
class="guess-number-game-new-game-button"
bind-event="
click:
$gameSeed($gameSeed + 1),
$lastGuess(0),
$attemptCount(0),
$minimumPossibleValue(1),
$maximumPossibleValue(100),
$currentGuessInput(50)
"
>
New game
</button>
</div>
<div class="guess-number-game-statistics">
14 collapsed lines
<div class="guess-number-game-statistic">
<span class="guess-number-game-statistic-label">tries</span>
<span class="guess-number-game-statistic-value" bind-text="$attemptCount"></span>
</div>
<div class="guess-number-game-statistic">
<span class="guess-number-game-statistic-label">remaining</span>
<span class="guess-number-game-statistic-value" bind-text="$maximumPossibleValue - $minimumPossibleValue + 1"></span>
</div>
<div class="guess-number-game-statistic">
<span class="guess-number-game-statistic-label">best</span>
<span class="guess-number-game-statistic-value" bind-text="$bestScore > 0 ? $bestScore : '—'"></span>
</div>
</div>
</wcp-ui-state>
</div>
<style>
204 collapsed lines
.guess-number-game {
max-inline-size: 400px;
margin: 0 auto;
font-family: var(--sb-md-font);
color: var(--sb-color-text);
}
.guess-number-game-header {
display: flex;
align-items: baseline;
justify-content: space-between;
padding-block-end: 1rem;
margin-block-end: 0.75rem;
border-block-end: 1px solid var(--sb-color-gray-6);
}
.guess-number-game-title {
font-size: var(--sb-text-xl);
font-weight: 700;
color: var(--sb-heading-color);
}
.guess-number-game-subtitle {
font-size: var(--sb-text-sm);
color: var(--sb-color-gray-3);
}
.guess-number-game-feedback {
display: flex;
align-items: center;
min-block-size: 48px;
padding: 0.5rem 1rem;
margin-block-end: 0.75rem;
font-size: var(--sb-text-sm);
background: var(--sb-color-gray-7);
border: 2px solid var(--sb-color-gray-5);
border-radius: var(--wcp-bdr-rad-md);
transition:
background 0.15s,
border-color 0.15s,
color 0.15s;
b {
font-weight: 700;
font-variant-numeric: tabular-nums;
}
}
.guess-number-game-feedback-higher {
color: var(--sb-note-text);
background: var(--sb-note-background);
border-color: var(--sb-note-border);
}
.guess-number-game-feedback-lower {
color: var(--sb-warning-text);
background: var(--sb-warning-background);
border-color: var(--sb-warning-border);
}
.guess-number-game-feedback-success {
font-weight: 600;
color: var(--sb-tip-text);
background: var(--sb-tip-background);
border-color: var(--sb-tip-border);
}
.guess-number-game-picker {
padding: 1.25rem 1rem 0.75rem;
margin-block-end: 0.75rem;
text-align: center;
background: var(--sb-color-gray-7);
border: 1px solid var(--sb-color-gray-6);
border-radius: var(--wcp-bdr-rad-md);
}
.guess-number-game-current-value {
display: block;
margin-block-end: 1rem;
font-size: var(--sb-text-5xl);
font-weight: 800;
font-variant-numeric: tabular-nums;
line-height: 1;
color: var(--sb-color-accent-high);
letter-spacing: -0.03em;
}
.guess-number-game-slider {
display: block;
inline-size: 99%;
accent-color: var(--sb-color-accent);
cursor: pointer;
&:disabled {
cursor: not-allowed;
opacity: 0.35;
}
}
.guess-number-game-range-bar {
position: relative;
block-size: 6px;
margin-block: 2em 0.4rem;
margin-inline: 3px;
overflow: hidden;
background: var(--sb-color-gray-5);
border-radius: 3px;
}
.guess-number-game-range-bar-fill {
position: absolute;
inset: 0;
inset-inline: calc(var(--range-start, 0) * 1%) calc((100 - var(--range-end, 100)) * 1%);
background: var(--sb-color-accent);
transition:
left 0.15s ease,
right 0.15s ease;
}
.guess-number-game-range-labels {
display: flex;
justify-content: space-between;
font-size: var(--sb-text-xs);
font-variant-numeric: tabular-nums;
color: var(--sb-color-gray-3);
}
.guess-number-game-actions {
display: grid;
grid-template-columns: 1fr auto;
gap: 0.5rem;
margin-block-end: 0.75rem;
}
.guess-number-game-guess-button,
.guess-number-game-new-game-button {
padding: 0.75rem 1.25rem;
font-family: var(--sb-md-font);
font-size: var(--sb-text-sm);
font-weight: 600;
letter-spacing: 0.03em;
cursor: pointer;
border: none;
border-radius: var(--wcp-bdr-rad-md);
transition:
opacity 0.1s,
background 0.1s;
}
.guess-number-game-guess-button {
color: var(--wcp-primary-content);
background: var(--sb-color-accent);
&:disabled {
cursor: default;
opacity: 0.3;
}
&:hover:not(:disabled) {
opacity: 0.85;
}
}
.guess-number-game-new-game-button {
color: var(--sb-color-gray-1);
background: var(--sb-color-gray-6);
border: 1px solid var(--sb-color-gray-5);
&:hover {
background: var(--sb-color-gray-5);
}
}
.guess-number-game-statistics {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1px;
overflow: hidden;
background: var(--sb-color-gray-5);
border-radius: var(--wcp-bdr-rad-md);
}
.guess-number-game-statistic {
display: flex;
flex-direction: column;
gap: 0.1rem;
align-items: center;
padding: 0.625rem 0.5rem;
background: var(--sb-color-gray-7);
}
.guess-number-game-statistic-label {
font-size: var(--sb-text-2xs);
color: var(--sb-color-gray-3);
text-transform: uppercase;
letter-spacing: 0.1em;
}
.guess-number-game-statistic-value {
font-size: var(--sb-text-xl);
font-weight: 700;
font-variant-numeric: tabular-nums;
color: var(--sb-color-gray-1);
}
</style>