DEV Community

NADJITIESSEM GONDJE Victoire
NADJITIESSEM GONDJE Victoire

Posted on

I built a spring-physics progress dot in vanilla JS and it taught me more about product craft than any framework ever did

Most side projects die in the details. Mine almost did too — but fixing one janky animation taught me what "craft" actually means in product work.

Here's what happened.


I'm building SkillForge — a learning roadmap generator. You type any skill, it generates a 5-7 phase curriculum with real YouTube links and tasks baked in. Phases unlock sequentially as you complete them.

The core UI is a zig-zag timeline with a dot that tracks your progress.

Simple idea. Brutal to execute.

Problem 1: The rigid dot

The dot tracked the vertical center of each phase card. When a user expanded a card to check off tasks, the card height changed, the center point shifted, and the dot jumped. Rigidly. Visibly. It looked broken.

Fix: I stopped tracking card centers entirely. I queried the DOM coordinates of the horizontal dash connector between phases instead. The dot now targets that fixed point regardless of what the card does beneath it.

Problem 2: Choppy animation during state changes

I was using CSS transitions. When a user expanded a task card while the dot was mid-movement, the transition stuttered. It looked cheap.

Fix: I stripped out CSS transitions completely and built a custom requestAnimationFrame loop with spring physics. Stiffness and damping applied to velocity. The dot now glides to its target and dynamically adjusts mid-flight if the UI shifts beneath it.

The code looks like this:

function animateDot() {
const dx = targetX - currentX;
const dy = targetY - currentY;

velocityX += dx * stiffness;
velocityY += dy * stiffness;
velocityX *= damping;
velocityY *= damping;

currentX += velocityX;
currentY += velocityY;

dot.style.left = currentX + 'px';
dot.style.top = currentY + 'px';

requestAnimationFrame(animateDot);
}

Nobody consciously notices this detail. But they feel it. That's the whole point.

Problem 3: The AI kept hallucinating timelines

I'm using Groq (Llama 4) to generate the roadmaps. It kept outputting "Week 1-3" for every single phase, or "Ongoing" for everything. Destroys the sequential feel.

Fix: Strict prompt constraints. "If Phase 1 is Week 1-3, Phase 2 MUST start Week 4. Never repeat a timeframe. Never use Ongoing." Force JSON output. Validate the structure before rendering.

Prompt engineering is just product thinking applied to language.

Problem 4: Exposed API key

First version had the Groq key sitting in frontend JavaScript. Obvious mistake in hindsight. I spun up a lightweight Node/Express proxy on Render to inject the key server-side. Users never touch it.


What I actually learned

Shipping a product is completely different from writing code.

Getting the AI to return data was easy. Getting it to return reliable, sequential, structured data was a product problem. Making a div move was easy. Making it move in a way that feels alive was a craft problem.

The difference between a demo and a product is a hundred small decisions that nobody asked for but everybody feels.

SkillForge is live if you want to try it: skillforge-cfcn.onrender.com

No account. No setup. Just type a skill and go.

What's the most painful UI detail you've ever had to fix?

Top comments (0)