There’s a version of this challenge that feels complete in minutes.
You click a quest.
It turns yellow.
Click again → green, strikethrough.
XP bar fills.
“LEVEL UP!” flashes.
It’s responsive. It’s clean. It works.
And yet… if you sit with it for a second, something starts to feel off.
Not visually.
Structurally.
The break doesn’t happen when something fails.
It happens when you ask a slightly annoying question:
“What actually controls this system?”
Because if the honest answer is “the DOM”… you’ve already lost.
Where the illusion begins
Most implementations don’t explicitly choose a bad approach.
They just drift into it.
A click handler like this:
el.classList.toggle("active");
feels harmless.
Then you stack another:
if (el.classList.contains("active")) {
xp += 33;
}
Now your logic is reading from the UI.
And just like that, the direction flips:
UI → Logic
Instead of:
State → UI
Nothing breaks immediately.
Which is why this pattern survives longer than it should.
The moment things get weird
Try this mentally:
- Click Quest 1 → Completed
- Click Quest 2 → Completed
- Refresh your understanding (not the page)
Now ask:
“How much XP do I have?”
If your system needs to inspect the DOM to answer that…
It doesn’t know its own state.
It’s guessing.
The version that feels different (and you can’t unsee it)
At some point, you stop touching the DOM entirely.
Not physically — but conceptually.
You start from something like this:
const game = {
quests: [
{ id: 1, state: "locked" },
{ id: 2, state: "locked" },
{ id: 3, state: "locked" }
],
clicks: 0
};
Nothing fancy.
But now there’s a single place where truth exists.
And that changes everything downstream.
Clicking a quest is no longer:
“change how this looks”
It becomes:
quest.state = nextState(quest.state);
A mutation of meaning, not appearance.
And then something subtle happens
You stop “updating” the XP bar.
You remove that code entirely.
Because XP is no longer something you store.
It becomes something you derive.
const completed = game.quests.filter(q => q.state === "completed").length;
const xp = (completed / game.quests.length) * 100;
No syncing.
No edge cases.
No “what if we forgot to update it?”
It just… exists.
This is where AI models start to split
On Vibe Code Arena, this challenge creates a really interesting pattern.
Not in who gets it working.
Everyone does.
But in how stable the system feels when you push it.
One model tends to scatter logic:
- click handler updates class
- another updates XP
- another checks for level-up
It’s like three mini-systems loosely cooperating.
And it holds… until you stress it.
Another model centralizes everything:
- state changes in one place
- rendering happens in one pass
- UI is always regenerated
Something like:
function render() {
// read state
// compute derived values
// output UI
}
That function becomes the entire app.
Not because it’s elegant.
Because it’s predictable.
The “LEVEL UP!” moment is not about animation
It’s where your system gets audited.
Because now you need to answer:
- When exactly does level-up happen?
- What resets?
- What persists?
- What triggers the animation?
If your logic is scattered, you end up with defensive code:
if (xp === 100 && !alreadyLeveled) {
// do something
}
If your logic is grounded in state, it’s almost boring:
if (xp === 100) {
level++;
resetQuests();
}
No guards. No hacks.
Just consequence.
The part that sneaks up on you
At some point, you stop thinking about:
- quests
- XP bars
- retro UI
And start thinking in transitions.
locked → active → completed → locked
That’s not UI logic.
That’s a state machine.
And once you see it that way…
Everything simplifies.
The UI becomes… replaceable
This is the weirdest shift.
You realize the visual layer doesn’t matter anymore.
Pixel font? Doesn’t matter.
Green vs yellow? Cosmetic.
Animation timing? Secondary.
Because the real system is already stable.
The UI is just a skin.
And this is where most people miss the actual challenge
They think they’re building:
“a retro quest tracker”
They’re actually being asked:
“Can you build a system where every visual outcome is inevitable?”
That’s a very different problem.
If you want to feel the difference, not just understand it
Open the challenge.
But don’t rush to finish it.
Break it a little.
Add one more quest.
Click things out of order.
Try to reason about your own system.
That’s where the gaps show up.
👉 https://vibecodearena.ai/share/6bf96add-8e8b-45de-ab7f-43a1124e9d12




Top comments (0)