<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: NADJITIESSEM GONDJE Victoire</title>
    <description>The latest articles on DEV Community by NADJITIESSEM GONDJE Victoire (@victoiren79hash).</description>
    <link>https://dev.to/victoiren79hash</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3977234%2F1a82610d-f221-4687-8321-0fadfa8adc94.png</url>
      <title>DEV Community: NADJITIESSEM GONDJE Victoire</title>
      <link>https://dev.to/victoiren79hash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victoiren79hash"/>
    <language>en</language>
    <item>
      <title>I built a spring-physics progress dot in vanilla JS and it taught me more about product craft than any framework ever did</title>
      <dc:creator>NADJITIESSEM GONDJE Victoire</dc:creator>
      <pubDate>Wed, 10 Jun 2026 08:10:18 +0000</pubDate>
      <link>https://dev.to/victoiren79hash/i-built-a-spring-physics-progress-dot-in-vanilla-js-and-it-taught-me-more-about-product-craft-than-482d</link>
      <guid>https://dev.to/victoiren79hash/i-built-a-spring-physics-progress-dot-in-vanilla-js-and-it-taught-me-more-about-product-craft-than-482d</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Here's what happened.&lt;/p&gt;




&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The core UI is a zig-zag timeline with a dot that tracks your progress.&lt;/p&gt;

&lt;p&gt;Simple idea. Brutal to execute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 1: The rigid dot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 2: Choppy animation during state changes&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The code looks like this:&lt;/p&gt;

&lt;p&gt;function animateDot() {&lt;br&gt;
  const dx = targetX - currentX;&lt;br&gt;
  const dy = targetY - currentY;&lt;/p&gt;

&lt;p&gt;velocityX += dx * stiffness;&lt;br&gt;
  velocityY += dy * stiffness;&lt;br&gt;
  velocityX *= damping;&lt;br&gt;
  velocityY *= damping;&lt;/p&gt;

&lt;p&gt;currentX += velocityX;&lt;br&gt;
  currentY += velocityY;&lt;/p&gt;

&lt;p&gt;dot.style.left = currentX + 'px';&lt;br&gt;
  dot.style.top = currentY + 'px';&lt;/p&gt;

&lt;p&gt;requestAnimationFrame(animateDot);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Nobody consciously notices this detail. But they feel it. That's the whole point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 3: The AI kept hallucinating timelines&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Prompt engineering is just product thinking applied to language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 4: Exposed API key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I actually learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Shipping a product is completely different from writing code.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The difference between a demo and a product is a hundred small decisions that nobody asked for but everybody feels.&lt;/p&gt;

&lt;p&gt;SkillForge is live if you want to try it: skillforge-cfcn.onrender.com&lt;/p&gt;

&lt;p&gt;No account. No setup. Just type a skill and go.&lt;/p&gt;

&lt;p&gt;What's the most painful UI detail you've ever had to fix?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
