<?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: Linards Liepenieks</title>
    <description>The latest articles on DEV Community by Linards Liepenieks (@linards_liepenieks).</description>
    <link>https://dev.to/linards_liepenieks</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%2F3393386%2Fe5981ecc-0eba-4a69-98fb-59b15788ec83.png</url>
      <title>DEV Community: Linards Liepenieks</title>
      <link>https://dev.to/linards_liepenieks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/linards_liepenieks"/>
    <language>en</language>
    <item>
      <title>Building Custom Scroll-Snap Sections: A Journey Through Mac Trackpad Hell 🖱️💀🔥</title>
      <dc:creator>Linards Liepenieks</dc:creator>
      <pubDate>Fri, 17 Oct 2025 12:00:20 +0000</pubDate>
      <link>https://dev.to/linards_liepenieks/building-custom-scroll-snap-sections-a-journey-through-mac-trackpad-hell-1k2k</link>
      <guid>https://dev.to/linards_liepenieks/building-custom-scroll-snap-sections-a-journey-through-mac-trackpad-hell-1k2k</guid>
      <description>&lt;p&gt;Ever wanted your website to do that fancy section-snap thing where each scroll smoothly transitions to the next full section? Like those Apple-esque landing pages that feel all premium and smooth?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's my advice: Don't do it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seriously. It's overengineered, difficult to navigate for users, and breaks expected browser behavior. Users hate when you mess with their scrolling. Accessibility tools hate it. Mobile users &lt;em&gt;really&lt;/em&gt; hate it. And you're about to spend way too much time fighting browser physics.&lt;/p&gt;

&lt;p&gt;But if you're like me and ignored this advice anyway (or your designer &lt;em&gt;really&lt;/em&gt; wants it, or it's actually justified for your specific use case), then buckle up. We're going deep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Gotcha: It Needs a Container 📦
&lt;/h2&gt;

&lt;p&gt;Before we even get to the momentum problem, here's something that'll bite you: &lt;strong&gt;you can't use default CSS scroll-snap on the page-level container&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You need a parent container with fixed height and overflow handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.scroll-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scroll-snap-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;mandatory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.section&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scroll-snap-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? Because we need programmatic control over the scrolling behavior, and the window object doesn't give us the same level of control as a DOM element. Plus, we need ref access for our scroll detection logic.&lt;/p&gt;

&lt;p&gt;This means restructuring your entire page. Already regretting this? Same.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem That Wouldn't Quit 😤
&lt;/h2&gt;

&lt;p&gt;"Just scrolling but no it needs to be a window." &lt;/p&gt;

&lt;p&gt;That's what I wrote in my notes after the third day of fighting with what I thought would be a simple feature. Here's what I wanted: User scrolls → page smoothly transitions to next section. Simple, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Narrator: It was not, in fact, a piece of cake.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what I wanted: User scrolls → page smoothly transitions to next section. Simple.&lt;/p&gt;

&lt;p&gt;Here's what I got: User scrolls once → page goes absolutely BONKERS and flies through 3-4 sections like it's trying to escape the viewport.&lt;/p&gt;

&lt;p&gt;The culprit? Mac trackpad momentum scrolling. That beautiful, smooth inertia effect that makes scrolling feel so natural on macOS? Yeah, that same feature was absolutely destroying my custom scroll implementation.&lt;/p&gt;

&lt;p&gt;When a Mac user swipes their trackpad, the browser doesn't just fire one scroll event. Oh no. It fires &lt;strong&gt;40+ events over 2-3 seconds&lt;/strong&gt;. Here's what my console looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🖱️ Wheel event: {deltaY: 4} → ✅ Scroll to section 2
🖱️ Wheel event: {deltaY: 132} → ✅ Scroll to section 3  
🖱️ Wheel event: {deltaY: 96} → ✅ Scroll to section 4
🖱️ Wheel event: {deltaY: 94} → ✅ Scroll to section 5
🖱️ Wheel event: {deltaY: 69} → 💥 WE'VE RUN OUT OF SECTIONS
... (35 more events that I'm desperately trying to ignore)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One. Single. Swipe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Naive Approaches (aka: Watch Me Fail Spectacularly) 🤦‍♂️
&lt;/h2&gt;

&lt;p&gt;Look, I know what you're thinking. "Just throttle it!" or "Use debounce!" Yeah, I tried that. And about five other things. Let me save you some time by showing you what &lt;em&gt;doesn't&lt;/em&gt; work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt #1: "Just add an event listener!"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wheel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;scrollToNextSection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: Page has seizure. Moving on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt #2: "Debouncing will fix it!"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;scrollToNextSection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: Events are already in the queue. Debouncing just delays the chaos. Still scrolls through multiple sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt #3: "Maybe Intersection Observer?"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: Better! But still doesn't prevent the momentum events from triggering new scrolls. The observer sees the section, but by then 30 more events have already fired.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempt #4: "Let me try FullPageJS"
&lt;/h3&gt;

&lt;p&gt;Me: &lt;em&gt;Imports 3rd party library&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Also me: &lt;em&gt;Realizes I still don't understand the problem&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Result: Same issue. Because the problem isn't the solution—it's my understanding.&lt;/p&gt;
&lt;h3&gt;
  
  
  Attempt #5: "What if I track cursor position?"
&lt;/h3&gt;

&lt;p&gt;Me: &lt;em&gt;Writes elaborate cursor tracking logic&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Reality: Wheel events don't have cursor position changes.&lt;br&gt;&lt;br&gt;
Me: 🤡&lt;/p&gt;
&lt;h3&gt;
  
  
  Attempt #6: "Throttling! That's the answer!"
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastScroll&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Result: Better! Page stops having seizures. But... 900ms feels random. Why 900ms? Why not 600ms? Or 1200ms? And sometimes momentum events still slip through. &lt;/p&gt;

&lt;p&gt;At this point I realized I was just slapping bandaids on symptoms without understanding the actual problem. Classic overengineering move—throwing solutions at a problem I didn't fully understand yet.&lt;/p&gt;

&lt;p&gt;I had console logs everywhere, a notebook full of timing diagrams, and a growing suspicion that I was fighting something fundamental about how trackpads work.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Breakthrough: Understanding Inertia 🌊
&lt;/h2&gt;

&lt;p&gt;After copious amounts of coffee and StackOverflow diving, I found the answer buried in a 2010 thread:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Since Mac spams scroll events with decreasing delta's to the browser every 20ms when inertial scrolling is enabled..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wait. &lt;strong&gt;Decreasing deltas?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;That's when it clicked. Momentum scrolling isn't random chaos—it's a &lt;strong&gt;pattern&lt;/strong&gt;. When you swipe a trackpad, the browser simulates physics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User swipes trackpad
→ Initial event: deltaY = 132 (high momentum)
→ Next event (13ms later): deltaY = 96 (decaying)
→ Next event (14ms later): deltaY = 94 (still decaying)
→ Next event (15ms later): deltaY = 69 (continuing to decay)
... pattern continues for 2-3 seconds until deltaY → 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The momentum events have a &lt;strong&gt;signature&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🏃 They fire rapidly (10-20ms apart)&lt;/li&gt;
&lt;li&gt;🔄 They're all in the same direction&lt;/li&gt;
&lt;li&gt;📉 The delta values decrease over time (momentum decay)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;And here's the kicker&lt;/strong&gt;: The web doesn't have a native API to detect this. &lt;/p&gt;

&lt;p&gt;I found out that native Mac apps get a &lt;code&gt;momentumPhase&lt;/code&gt; property on scroll events. The web? We get &lt;code&gt;deltaX&lt;/code&gt;, &lt;code&gt;deltaY&lt;/code&gt;, and basically nothing else. No momentum detection. No inertia flag. Nada.&lt;/p&gt;

&lt;p&gt;So here I am, overengineering a scroll experience, and the platform doesn't even give me the tools to do it properly. Beautiful. The entire web development community has been reverse-engineering momentum detection from first principles because the web platform said "figure it out yourself lol."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Delta Pattern Analysis 🔬
&lt;/h2&gt;

&lt;p&gt;Armed with this knowledge, I built a momentum detector based on three conditions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A scroll event is MOMENTUM (not intentional) if ALL of these are true:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isTimeTooQuick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeSinceLastEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Less than 1.5s since last event&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSameDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDirection&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastDirection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Same scroll direction&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDeltaNotIncreasing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;lastDelta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Delta not getting bigger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me break down why each condition matters:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Time Gap Detection ⏱️
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeSinceLastEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastEventTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isTimeTooQuick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeSinceLastEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If events are firing less than 1.5 seconds apart, they're likely part of the same momentum cascade. A human user won't intentionally scroll again that quickly—they're waiting for the animation to finish.&lt;/p&gt;

&lt;p&gt;Real log showing momentum events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timeSinceLastEvent: 13ms  ← TOO FAST, must be momentum
timeSinceLastEvent: 14ms  ← Still too fast
timeSinceLastEvent: 15ms  ← Definitely momentum
timeSinceLastEvent: 3547ms ← Enough time passed, could be intentional
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Direction Consistency 🧭
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1 or -1&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSameDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDirection&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastDirection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Momentum events continue in the same direction. If the user suddenly scrolls the opposite way, that's intentional—they're fighting the momentum to change direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Delta Decay Pattern 📉
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDeltaNotIncreasing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;lastDelta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;strong&gt;most important indicator&lt;/strong&gt;. Momentum naturally decays due to physics simulation. If the delta is increasing, the user is applying more force—that's intentional.&lt;/p&gt;

&lt;p&gt;Here's a real momentum cascade from my logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;   &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s initial swipe (intentional) ✅
deltaY: 132 ← Momentum starts strong 🌊
deltaY: 96  ← Decaying ⬇️
deltaY: 94  ← Still decaying ⬇️
deltaY: 69  ← Continuing to decay ⬇️
deltaY: 57  ← Getting smaller ⬇️
deltaY: 53  ← Even smaller ⬇️
deltaY: 49  ← Approaching zero ⬇️
deltaY: 45  ← Almost done ⬇️
... continues for another 30+ events
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the pattern? After the user's initial intentional scroll (deltaY: 4), the momentum kicks in with a burst (132) and then steadily decreases. &lt;strong&gt;That decreasing pattern is the fingerprint of inertia&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Complete Detection Logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useTouchpadDetection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;minTimeGap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;minDelta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;UseTouchpadDetectionProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastDeltaRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastDirectionRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastEventTimeRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detectInertia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WheelEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeSinceLastEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastEventTimeRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Check all three conditions&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isTimeTooQuick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeSinceLastEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;minTimeGap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSameDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
      &lt;span class="nx"&gt;lastDirectionRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
      &lt;span class="nx"&gt;currentDirection&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastDirectionRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDeltaNotIncreasing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;lastDeltaRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMomentumScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
      &lt;span class="nx"&gt;isTimeTooQuick&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
      &lt;span class="nx"&gt;isSameDirection&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
      &lt;span class="nx"&gt;isDeltaNotIncreasing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Update tracking for next event&lt;/span&gt;
    &lt;span class="nx"&gt;lastDeltaRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;lastDirectionRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDirection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;lastEventTimeRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isMomentumScroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;detectInertia&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adaptive Throttling: The Final Piece 🎯
&lt;/h2&gt;

&lt;p&gt;Once we can detect momentum, we can apply &lt;strong&gt;adaptive throttling&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NORMAL_THROTTLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Intentional scrolls - be responsive&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MOMENTUM_THROTTLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Momentum scrolls - block everything&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effectiveThrottle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isMomentumScroll&lt;/span&gt; 
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;MOMENTUM_THROTTLE&lt;/span&gt; 
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NORMAL_THROTTLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why two different values?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normal throttle (600ms)&lt;/strong&gt;: When a user intentionally scrolls, we want to be responsive. 600ms is enough to prevent accidental double-scrolls but still feels snappy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Momentum throttle (1800ms)&lt;/strong&gt;: When we detect momentum, we need to block events for the entire duration of the momentum cascade (typically 2-3 seconds). 1800ms covers most of this.&lt;/p&gt;

&lt;p&gt;Here's the flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User swipes trackpad
  ↓
Event 1: deltaY = 4 (intentional)
  → ✅ Scroll to next section
  → 🔒 Set 600ms throttle
  ↓
Event 2: deltaY = 132 (13ms later - MOMENTUM!)
  → ❌ Blocked by throttle
  → 🔒 Extend to 1800ms throttle
  ↓
Events 3-40: All momentum
  → ❌ All blocked by 1800ms throttle
  ↓
1800ms passes, momentum events stop
  ↓
User scrolls again
  → ✅ Accepted (enough time has passed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Edge Cases That Almost Broke Everything 🐛
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bug #1: Equal Consecutive Deltas
&lt;/h3&gt;

&lt;p&gt;My first version checked &lt;code&gt;currentDelta &amp;lt; lastDelta&lt;/code&gt;. But look what happened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deltaY: 28
deltaY: 28 ← SAME VALUE!
deltaY: 24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When two momentum events had the same delta (28 → 28), my check failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;isDeltaDecreasing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// false! ❌&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix: Use &lt;code&gt;&amp;lt;=&lt;/code&gt; instead of &lt;code&gt;&amp;lt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDeltaNotIncreasing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDelta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;lastDelta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true! ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bug #2: Console Logs Were Slowing Everything Down
&lt;/h3&gt;

&lt;p&gt;I had debug logs everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🖱️ Wheel event:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;timeSinceLastEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;isTimeTooQuick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;isSameDirection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 10 more properties&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each console.log adds I/O overhead. With 40+ events per scroll, this was adding &lt;strong&gt;significant&lt;/strong&gt; latency. The solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Toggle for development&lt;/span&gt;

&lt;span class="nx"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🖱️ Wheel event:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production performance improved dramatically just by removing console logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug #3: First Event After Direction Change
&lt;/h3&gt;

&lt;p&gt;When a user scrolled down, then up, the first "up" event would fail detection because:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSameDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentDirection&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastDirectionRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// -1 === 1 → false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is actually correct behavior! A direction change indicates intentional user input. But it taught me that edge cases in momentum detection are features, not bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Architecture 🏗️
&lt;/h2&gt;

&lt;p&gt;I split the logic into three composable hooks:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. useTouchpadDetection
&lt;/h3&gt;

&lt;p&gt;Detects momentum using delta pattern analysis&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;detectInertia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValidDelta&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTouchpadDetection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;minTimeGap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minDelta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. useScrollThrottle
&lt;/h3&gt;

&lt;p&gt;Manages adaptive throttling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isThrottled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateThrottle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useScrollThrottle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;normalThrottle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;momentumThrottle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. useScrollContainer
&lt;/h3&gt;

&lt;p&gt;Orchestrates everything&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;throttledWheelHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WheelEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMomentumScroll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;detectInertia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isValidDelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isScrollingRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isThrottled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMomentumScroll&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentSection&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextSection&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;nextSection&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;totalSections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;scrollToSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextSection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, testable, and composable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned (The Hard Way) 📝
&lt;/h2&gt;

&lt;p&gt;Let me be real with you for a second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Seriously, don't do custom scroll-snapping unless you really need to&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I spent a week on this. A week. For a feature that breaks browser expectations and annoys users. Was it worth it? Debatable. Did I learn a ton? Absolutely. Would I do it again? Only if someone's paying me well or there's a &lt;em&gt;really&lt;/em&gt; good UX reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The web platform has surprising gaps&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Native apps get &lt;code&gt;momentumPhase&lt;/code&gt;. Web developers get to reverse-engineer physics. This is kind of insane considering how fundamental scrolling is to web interaction. But hey, at least we get to feel smart when we solve it, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Console logs aren't free&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In high-frequency events like scrolling, logging overhead is real. I was wondering why my throttling felt sluggish—turns out 40+ console.logs per scroll adds up. Always gate debug logs behind a flag in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Pattern recognition beats magic numbers&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
My first instinct was "throttle everything for 900ms!" But understanding the &lt;em&gt;why&lt;/em&gt; behind momentum patterns led to a way better solution with adaptive throttling. Still overengineered? Maybe. But at least it's &lt;em&gt;informed&lt;/em&gt; overengineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Physics leaves fingerprints&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The decreasing delta pattern isn't arbitrary—it's a simulation of real-world physics. Once I understood that, the solution became obvious. Well, obvious in hindsight after days of confusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Sometimes the simple stuff is the hardest&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
"Just scroll to the next section" turned into a week-long journey through browser APIs, momentum physics, and performance optimization. Never underestimate the fundamentals. They'll humble you real quick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Professional libraries use the same approach&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
After building all this, I found &lt;a href="https://github.com/xiel/wheel-gestures" rel="noopener noreferrer"&gt;wheel-gestures&lt;/a&gt; uses nearly identical logic. So either I'm as smart as library authors, or we're all equally overengineering the same problem. Probably the latter.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Results 🎉
&lt;/h2&gt;

&lt;p&gt;After implementing delta pattern analysis with adaptive throttling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Smooth, intentional scrolling on all devices&lt;/li&gt;
&lt;li&gt;✅ Zero accidental section skips on Mac trackpads
&lt;/li&gt;
&lt;li&gt;✅ Responsive feel (600ms) for intentional scrolls&lt;/li&gt;
&lt;li&gt;✅ Complete momentum blocking (1800ms) when detected&lt;/li&gt;
&lt;li&gt;✅ Works with touch events, mouse wheels, and trackpads&lt;/li&gt;
&lt;li&gt;✅ Clean, composable architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The page finally scrolls exactly as intended. One swipe, one section. Every time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resources 📚
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/4339196/how-to-detect-disable-inertial-scrolling-in-mac-safari" rel="noopener noreferrer"&gt;StackOverflow: Detecting inertial scrolling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/xiel/wheel-gestures" rel="noopener noreferrer"&gt;wheel-gestures library&lt;/a&gt; - Professional implementation&lt;/li&gt;
&lt;li&gt;
My full implementation on GitHub &lt;em&gt;(add your link)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/HandlingTouchEvents/HandlingTouchEvents.html" rel="noopener noreferrer"&gt;Apple's Handling Trackpad Events&lt;/a&gt; - How native apps do it&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Final Thoughts 💭
&lt;/h2&gt;

&lt;p&gt;If you're building custom scroll interactions, momentum detection isn't optional—it's essential. The web doesn't give you the tools out of the box, so you gotta build them yourself.&lt;/p&gt;

&lt;p&gt;The good news? Once you understand the pattern (fast events + same direction + decreasing deltas = momentum), the solution is pretty straightforward.&lt;/p&gt;

&lt;p&gt;The bad news? You're probably going to spend a few days discovering this the hard way, just like I did. And then you'll question whether the whole thing was worth it in the first place. (It probably wasn't, but at least you learned something, right?)&lt;/p&gt;

&lt;p&gt;But hey, at least now you know why that random 900ms throttle everyone suggests feels so arbitrary. It wasn't the solution—it was just a bandaid on the real problem.&lt;/p&gt;


&lt;h2&gt;
  
  
  📦 Get the Code
&lt;/h2&gt;

&lt;p&gt;Ready to use this in your project?&lt;/p&gt;
&lt;h3&gt;
  
  
  🎮 Try it Live
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://react-scroll-snap-momentum.vercel.app/demo" rel="noopener noreferrer"&gt;Interactive Demo&lt;/a&gt;&lt;/strong&gt; - Experience the smooth scrolling with your own trackpad&lt;/p&gt;
&lt;h3&gt;
  
  
  💾 Download Options
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option 1: GitHub&lt;/strong&gt; (Free, instant access)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/LinardsLiepenieks/react-scroll-snap-momentum.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⭐ &lt;a href="https://github.com/LinardsLiepenieks/react-scroll-snap-momentum" rel="noopener noreferrer"&gt;Star the repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2: Complete Package&lt;/strong&gt; (Free, get notified of updates)&lt;br&gt;
📦 &lt;a href="https://linardsliep.gumroad.com/l/react-scroll-snap" rel="noopener noreferrer"&gt;Download on Gumroad&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Gumroad package includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ All 4 hooks with TypeScript&lt;/li&gt;
&lt;li&gt;✅ 2 working examples (basic + routing)&lt;/li&gt;
&lt;li&gt;✅ Full documentation&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Free 20-minute consultation&lt;/strong&gt; to help you implement it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both options are 100% free. Gumroad just adds you to the updates list so you'll know when I add new features!&lt;/p&gt;




&lt;h2&gt;
  
  
  📬 Let's Connect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Need help implementing this?&lt;/strong&gt; I offer a &lt;strong&gt;free 20-minute consultation&lt;/strong&gt; to get you started.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💼 &lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/linards-liepenieks/" rel="noopener noreferrer"&gt;Linards Liepenieks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📧 &lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:linardsliepenieks@gmail.com"&gt;linardsliepenieks@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐛 &lt;strong&gt;Issues/Questions:&lt;/strong&gt; &lt;a href="https://github.com/LinardsLiepenieks/react-scroll-snap-momentum/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Have you fought with momentum scrolling? Found a different approach? Or did you wisely decide not to mess with browser defaults?&lt;/strong&gt; Drop a comment—I'd love to hear about your experience (or your decision to avoid this entire mess).&lt;/p&gt;

&lt;p&gt;And if this saved you a few days of debugging, consider sharing it with someone who's about to learn about Mac trackpad momentum the hard way. Misery loves company. 😅&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S. - If your PM asks for scroll-snapping, show them this article first. Maybe they'll reconsider. Probably not. But at least you tried.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  webdev #javascript #react #performance #ux
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Metadata rabbithole for beginners 🐇🕳️</title>
      <dc:creator>Linards Liepenieks</dc:creator>
      <pubDate>Mon, 28 Jul 2025 08:56:01 +0000</pubDate>
      <link>https://dev.to/linards_liepenieks/metadata-rabbithole-for-beginners-58p</link>
      <guid>https://dev.to/linards_liepenieks/metadata-rabbithole-for-beginners-58p</guid>
      <description>&lt;p&gt;If you'd asked me about metadata a week ago, I would have confidently said "oh yeah, those HTML tags for SEO" and moved on with my life. 🤷‍♂️&lt;br&gt;
I was perfectly content in my web development bubble. Building React frontends, teaching Python to teenagers, and occasionally wrestling with CSS grid layouts.&lt;/p&gt;

&lt;p&gt;Then I made a mistake that changed everything.&lt;/p&gt;

&lt;p&gt;We were pivoting at my startup when we got a meeting with the National Archives of *******. &lt;br&gt;
I walked in ready to talk about how AI could revolutionize their "document processing workflows" and "automate their daily tasks", but they hit me with something that left me with my mouth open:&lt;/p&gt;

&lt;p&gt;"Scanning works fine. The main problem is metadata."&lt;/p&gt;

&lt;p&gt;I nodded along like I totally understood, but internally I was like... wait, what? 😅&lt;/p&gt;

&lt;p&gt;What even is that?&lt;/p&gt;

&lt;p&gt;Metadata is data about data. But that definition is about as helpful as saying "programming is writing code."&lt;/p&gt;

&lt;p&gt;Metadata is like the invisible Post-it notes stuck to everything digital, telling systems what something is, when it was created, and how to handle it 📝.&lt;/p&gt;

&lt;p&gt;Metadata isn't just background noise, it is what makes things findable, sortable, and actually useful.&lt;/p&gt;

&lt;p&gt;Think about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without metadata, Google would just be expensive text matching&lt;/li&gt;
&lt;li&gt;Without metadata, your database queries would crawl through every record&lt;/li&gt;
&lt;li&gt;Without metadata, Netflix would show you random movies instead of recommendations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The crazy part? We use it constantly without realizing. &lt;/p&gt;

&lt;p&gt;git commit? &lt;/p&gt;

&lt;p&gt;That's creating metadata. &lt;/p&gt;

&lt;p&gt;Database indexes? &lt;/p&gt;

&lt;p&gt;Metadata. &lt;/p&gt;

&lt;p&gt;Alt tags? &lt;/p&gt;

&lt;p&gt;Also metadata 💡.&lt;/p&gt;

&lt;p&gt;First thing to understand is like any technology, metadata has multiple frameworks with their pros and cons. Here are the four that matter most:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dublin Core 📚&lt;/strong&gt;&lt;br&gt;
The universal standard for describing any digital resource with 15 basic elements. Maintained by the Dublin Core Metadata Initiative. Used everywhere - local libraries to government archives because it's simple enough for anyone to implement but comprehensive enough to describe virtually anything.&lt;/p&gt;

&lt;p&gt;Think of it as the minimum metadata. Use it when you need basic discoverability without complexity.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;dc:title&amp;gt;Introduction to Machine Learning&amp;lt;/dc:title&amp;gt;&lt;br&gt;
&amp;lt;dc:creator&amp;gt;Jane Smith&amp;lt;/dc:creator&amp;gt;&lt;br&gt;
&amp;lt;dc:date&amp;gt;2024-07-15&amp;lt;/dc:date&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema.org 🔍&lt;/strong&gt;&lt;br&gt;
A structured vocabulary that tells search engines what your web content actually means. Maintained by Google, Microsoft, Yahoo, and Yandex. This is how Google can show recipe cards and event details directly in search results—it understands the content structure, not just keywords.&lt;/p&gt;

&lt;p&gt;It works by embedding structured data directly in your HTML. Use it when you want search engines to display rich snippets.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;div itemscope itemtype="https://schema.org/Recipe"&amp;gt;&lt;br&gt;
  &amp;lt;h1 itemprop="name"&amp;gt;Best Pizza Recipe&amp;lt;/h1&amp;gt;&lt;br&gt;
  &amp;lt;span itemprop="cookTime"&amp;gt;PT30M&amp;lt;/span&amp;gt;&lt;br&gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PREMIS 🏛️&lt;/strong&gt;&lt;br&gt;
Digital preservation standard that tracks every detail of a file's lifecycle - creation, migration and access events. Maintained by the Library of Congress. Museums and archives use this as digital files degrade and become unreadable over time without proper tracking and migration.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;premis:object&amp;gt;&lt;br&gt;
  &amp;lt;premis:objectIdentifier&amp;gt;&lt;br&gt;
    &amp;lt;premis:objectIdentifierType&amp;gt;local&amp;lt;/premis:objectIdentifierType&amp;gt;&lt;br&gt;
    &amp;lt;premis:objectIdentifierValue&amp;gt;document_001.pdf&amp;lt;/premis:objectIdentifierValue&amp;gt;&lt;br&gt;
  &amp;lt;/premis:objectIdentifier&amp;gt;&lt;br&gt;
&amp;lt;/premis:object&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EXIF 📸&lt;/strong&gt;&lt;br&gt;
Standard that automatically embeds camera settings, location, and timestamps into every photo you take. Maintained by the Camera &amp;amp; Imaging Products Association (CIPA). This exists as photos without context are nearly useless, you need to know when, where, and how they were captured.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;exif:DateTime&amp;gt;2024:07:28 14:30:22&amp;lt;/exif:DateTime&amp;gt;&lt;br&gt;
&amp;lt;exif:GPSLatitude&amp;gt;60.1699&amp;lt;/exif:GPSLatitude&amp;gt;&lt;br&gt;
&amp;lt;exif:Make&amp;gt;Apple&amp;lt;/exif:Make&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Practical example: Reading PDF Metadata 🛠️&lt;br&gt;
Now that we know the standards exist, let's see them in action. Here's how to extract Dublin Core metadata from any PDF:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr149g84nxd801eed0okn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr149g84nxd801eed0okn.png" alt=" " width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrtlzrmarhpfs6cvwd59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrtlzrmarhpfs6cvwd59.png" alt=" " width="598" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember that awkward moment with the National Archives? Turns out they were completely right.&lt;/p&gt;

&lt;p&gt;At my startup (&lt;a href="https://www.linkedin.com/company/djanbee/?viewAsMember=true" rel="noopener noreferrer"&gt;Djanbee&lt;/a&gt;😊), we're pivoting hard to the metadata space. &lt;br&gt;
We are realizing the real problem isn't scanning documents, it'sfinding them.&lt;/p&gt;

&lt;p&gt;Now when I organize my photos or write commit messages, I think about the invisible information that makes things actually work 🔍.&lt;/p&gt;

&lt;p&gt;Metadata isn't sexy, but once you see it, you can't unsee it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What do you think? Let me know in the comments! 👇&lt;/em&gt;&lt;/p&gt;

</description>
      <category>metadata</category>
      <category>automation</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
