PageSourceSearch

https://pqina.nl/blog/css-smooth-scrolling/

html pqina.nl collected 2026-09-24 17:54:39 UTC 25,297 bytes, 517 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en-US">
3<head>
4
5    <meta charset="UTF-8">
6    <meta name="author" content="Pqina">
7    <meta name="viewport" content="width=device-width">
8    <meta name="theme-color" content="#ffffff">
9    <meta name="description" content="Last year, while building the FilePond product page I stumbled upon the scrollIntoView API. It’s a handy method to instruct the browser to scroll an element into the viewport.">
10
11    <title>CSS Smooth Scrolling - Pqina</title>
12
13    <link rel="canonical" href="https://pqina.nl/blog/css-smooth-scrolling/"/>
14    <link rel="shortcut icon" href="/favicon.ico" />
15    <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
16    <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
17
18    <meta property="og:type" content="website">
19    <meta property="og:url" content="https://pqina.nl/blog/css-smooth-scrolling/">
20    <meta property="og:image" content="https://pqina.nl/cover/css-smooth-scrolling.jpeg">
21    <meta property="og:site_name" content="Pqina">
22    <meta property="og:title" content="CSS Smooth Scrolling">
23    <meta property="og:description" content="Last year, while building the FilePond product page I stumbled upon the scrollIntoView API. It’s a handy method to instruct the browser to scroll an element into the viewport.">
24
25    <meta name="twitter:site" content="@pqina">
26    <meta name="twitter:creator" content="@rikschennink">
27    <meta name="twitter:title" content="CSS Smooth Scrolling">
28    <meta name="twitter:description" content="Last year, while building the FilePond product page I stumbled upon the scrollIntoView API. It’s a handy method to instruct the browser to scroll an element into the viewport.">
29    <meta name="twitter:image" content="https://pqina.nl/cover/css-smooth-scrolling.jpeg">
30    <meta name="twitter:card" content="summary_large_image">
31
32    <link href="https://pqina.nl/feed.xml" rel="alternate" type="application/rss+xml" title="RSS">
33
34    
34<script>
35        // sync theme from storage
36        try {
37            const theme = localStorage.getItem('theme');
38            if (theme) {
39                document.documentElement.dataset.theme = theme;
40            }
41            else {
42                const mql = window.matchMedia('(prefers-color-scheme: dark)');
43                document.documentElement.dataset.theme = mql.matches ? 'dark' : 'bright';
44            }
45        }
46        catch(err){}
47    </script>
47
48
49    
50    <style>
51        :where(body, iframe, pre, img, svg, video, canvas, select, input) {
52            max-width: 100%;
53            overflow: auto;
54            word-break: break-word;
55        }
56
57        :where(ul, ol) {
58            padding-left: 1rem;
59        }
60
61        html,
62        body {
63            margin: 0;
64            padding: 0;
65        }
66
67        body {
68            line-height: 1.25;
69        }
70
71
72        main {
73            margin: 0 auto;
74            padding: 1rem;
75            max-width: 40rem;
76        }
77
78        theme-switch {
79            display: none;
80        }
81
82        .header-exit {
83            display: none;
84        }
85
86        .article-cover {
87            display:none;
88        }
89    </style>
90
91    <link href="/static/css/styles.css" rel="stylesheet">
92    <link href="/static/assets/fonts/Lato-Bold.css" rel="stylesheet">
93</head>
94
95<body class="layout-post">
96
97    
98    <main>
99    
100<a class="logo" href="/">
101    Pqina
102</a>
103
104
105<h1>CSS Smooth Scrolling</h1>
106
107
108<time datetime="2019-02-14T00:00:00.000Z">February 14th, 2019</time>
109
110<div class="article">
111    
112    <p>Last year, while building the <a href="https://pqina.nl/filepond/?ref=pqina">FilePond product page</a> I stumbled upon the scrollIntoView API. It’s a handy method to instruct the browser to scroll an element into the viewport.</p>
113<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView"><code>scrollIntoView</code></a> API can be instructed to animate the scrolling part by adding the <code>behavior</code> property on the <code>scrollIntoViewOption</code> object.</p>
114<pre class="language-js"><code class="language-js">element<span class="token punctuation">.</span><span class="token function">scrollIntoView</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">behavior</span><span class="token operator">:</span> <span class="token string">'smooth'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
115<p>I quickly jumped on my JavaScript horse and wrote a tiny script to automatically detect clicks on anchors so the browser would animate the jump towards the anchor target. This jump can be really disorienting, so animating this process would improve the user experience quite a bit.</p>
116<p><a href="https://caniuse.com/#feat=css-scroll-behavior">scrollIntoViewOption currently only works on Firefox and Chrome</a>.</p>
117<pre class="language-js"><code class="language-js"><span class="token comment">// Everytime someone clicks on something</span>
118document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
119    <span class="token keyword">const</span> href <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>href<span class="token punctuation">;</span>
120
121    <span class="token comment">// no href attribute, no need to continue then</span>
122    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>href<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
123
124    <span class="token keyword">const</span> id <span class="token operator">=</span> href<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'#'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
125    <span class="token keyword">const</span> target <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
126
127    <span class="token comment">// no target to scroll to, bail out</span>
128    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>target<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
129
130    <span class="token comment">// prevent the default quick jump to the target</span>
131    e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
132
133    <span class="token comment">// set hash to window location so history is kept correctly</span>
134    history<span class="token punctuation">.</span><span class="token function">pushState</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> document<span class="token punctuation">.</span>title<span class="token punctuation">,</span> href<span class="token punctuation">)</span><span class="token punctuation">;</span>
135
136    <span class="token comment">// smooooooth scroll to the target!</span>
137    target<span class="token punctuation">.</span><span class="token function">scrollIntoView</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
138        <span class="token literal-property property">behavior</span><span class="token operator">:</span> <span class="token string">'smooth'</span><span class="token punctuation">,</span>
139        <span class="token literal-property property">block</span><span class="token operator">:</span> <span class="token string">'start'</span><span class="token punctuation">,</span>
140    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
141<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
142<p><a href="https://codepen.io/rikschennink/details/ZjBGda">Live demo</a></p>
143<p>This works wonderfully.</p>
144<p>I <a href="https://twitter.com/rikschennink/status/1019960938194259976">posted it on Twitter</a> and called it a day.</p>
145<p>Then, <a href="https://twitter.com/hans_/status/1020081742277902336">Hans Spieß points out that this can also be done with CSS</a>, WHAT!?</p>
146<p>Turns out there’s a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior"><code>scroll-behavior</code></a> CSS property that we can set to <code>smooth</code>, it’s literally that literal. It’s almost like <code>awesome: yes-please</code>. We can set the <code>scroll-behavior</code> property to the container we want to exhibit smooth scroll behavior and we’re done.</p>
147<p>I created a new <a href="https://codepen.io/rikschennink/details/NBBZWO">demo on CodePen</a> using only CSS.</p>
148<video autoplay loop muted playsinline>
149    <source src="/media/scroll-smooth-diff.mp4" type="video/mp4">
150</video>
151<p>Before we go nuts and apply this to all our sites, there are a couple of things to keep in mind.</p>
152<h3 id="scroll-distance-matters">Scroll distance matters</h3>
153<p>If there is a lot of content to scroll, Firefox will skip content to keep the scroll time-limited, while Chrome has a max velocity and will just take its time to get to the target.</p>
154<video autoplay loop muted playsinline>
155    <source src="/media/scroll-long-distance.mp4" type="video/mp4">
156</video>
157<p><a href="https://codepen.io/rikschennink/details/QBZByx">Live Demo on CodePend</a></p>
158<p>We could use <a href="/blog/applying-styles-based-on-the-user-scroll-position-with-smart-css/">Smart CSS</a> to detect long pages and conditionally apply the smooth scroll style.</p>
159<h3 id="accessibility">Accessibility</h3>
160<p>People might get motion sickness when watching the animation. To circumvent this you can wrap the CSS property in a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion"><code>prefers-reduced-motion</code></a> media query. Unfortunately, Chrome does not support this. Safari supports it, but Safari doesn’t support smooth scrolling.</p>
161<pre class="language-css"><code class="language-css"><span class="token selector">.my-smooth-container</span> <span class="token punctuation">{</span>
162    <span class="token property">scroll-behavior</span><span class="token punctuation">:</span> smooth<span class="token punctuation">;</span>
163<span class="token punctuation">}</span>
164
165<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-reduced-motion</span><span class="token punctuation">:</span> reduce<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
166    <span class="token selector">.my-smooth-container</span> <span class="token punctuation">{</span>
167        <span class="token property">scroll-behavior</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
168    <span class="token punctuation">}</span>
169<span class="token punctuation">}</span></code></pre>
170<h2 id="conclusion">Conclusion</h2>
171<p>When deciding on a new functionality we shouldn’t reach out to JavaScript immediately. We should do a quick search first to find out if it can be done with CSS as well. The <code>scroll-behavior</code> property can be a nice UX improvement, do make sure you disable it on very long pages and offer an option to disable it to keep your pages accessible.</p>
172
173</div>
174
175<script>
176Array.from(document.getElementsByTagName('img')).forEach(img => {
177    img.dataset.state = img.complete ? 'complete' : 'loading';
178    img.onload = () => img.dataset.state = 'complete';
179});
180</script>
180
181
182<div class="author">
183
184    <div class="sig">
185  <a href="https://bsky.app/profile/rikschennink.com" target="_blank">
186    <img src="/static/assets/sig.svg" alt="Rik Schennink" width="200" height="34" loading="lazy"/>
187  </a>
188</div>
189
190    <p class="author-about">I share web dev tips on Bluesky, if you found this interesting and want to learn more, <a href="https://bsky.app/profile/rikschennink.com" target="_blank">follow me there<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" width="16" height="16"><title id="atgjgum5l405zuuwt01trburfb0fpvf7">Bluesky</title><path fill="currentColor" d="M3.468 1.948C5.303 3.325 7.276 6.118 8 7.616c.725-1.498 2.697-4.29 4.532-5.668C13.855.955 16 .186 16 2.632c0 .489-.28 4.105-.444 4.692-.572 2.04-2.653 2.561-4.504 2.246 3.236.551 4.06 2.375 2.281 4.2-3.376 3.464-4.852-.87-5.23-1.98-.07-.204-.103-.3-.103-.218 0-.081-.033.014-.102.218-.379 1.11-1.855 5.444-5.231 1.98-1.778-1.825-.955-3.65 2.28-4.2-1.85.315-3.932-.205-4.503-2.246C.28 6.737 0 3.12 0 2.632 0 .186 2.145.955 3.468 1.948Z"></path></svg></a>
191    </p>
192
193    <p>Or join my newsletter</p>
194
195    <async-form>
196    <form class="newsletter form" action="https://pqina.nl/api/v1/subscribe" method="POST">
197
198        
199        <input type="text" 
200            name="firstname"
201            style="display:none !important" tabindex="-1" autocomplete="off">
202        
203
204        <div class="row">
205
206            <div class="field">
207                <label for="email" class="implicit">Email address</label>
208                <input required type="email" id="email" name="email" placeholder="[email protected]">
209            </div>
210
211            <button class="button button-cta" type="submit">Let's go!</button>
212
213        </div>
214        
215        <p class="form-status-busy">Busy doing the newsletter subscribing.</p>
216        
217        <p class="form-status-error">Something went wrong, can you give it another go?</p>
218
219        <p class="form-status-success">Nearly there, I've just sent you an email to confirm your subscription.</p>
220
221    </form>
222</async-form>
223
224</div>
225
226<div class="promoted-products">
227    <p>At Pqina I design and build highly polished web components.</p>
228
229    <p>Make sure to check out <a href="/filepond/?ref=pqina">FilePond</a> a free file upload component, and <a href="/pintura/?ref=pqina">Pintura</a> an image editor that works on every device.</p>
230</div>
231
232
233
234
235
236<div class="related">
237    <h2>Related articles</h2>
238    <ul class="styled-list">
239
240    
241    
242        
243        <li>
244            <div class="article-item">
245
246    <h3 class="article-title">
247        <a href="/blog/animating-width-and-height-without-the-squish-effect/">Animating CSS Width And Height Without The Squish Effect</a>
248    </h3>
249
250    
251    <time datetime="2020-01-27T00:00:00.000Z">January 27th, 2020</time>
252
253    
254
255</div>
256        </li>
257        
258    
259        
260        <li>
261            <div class="article-item">
262
263    <h3 class="article-title">
264        <a href="/blog/update-container-border-radius-scrollbar-overflow/">Updating CSS Border Radius When A Container Is Overflowing</a>
265    </h3>
266
267    
268    <time datetime="2025-11-26T00:00:00.000Z">November 26th, 2025</time>
269
270    
271
272</div>
273        </li>
274        
275    
276        
277        <li>
278            <div class="article-item">
279
280    <h3 class="article-title">
281        <a href="/blog/blocking-navigation-gestures-on-ios-13-4/">Blocking Navigation Gestures On iOS Safari</a>
282    </h3>
283
284    
285    <time datetime="2020-06-10T00:00:00.000Z">June 10th, 2020</time>
286
287    
288
289</div>
290        </li>
291        
292    
293    </ul>
294</div>
295
296
297<a class="footer-exit" href="/#articles">More articles</a>
298
299<a class="header-exit" href="/#articles">
300    <svg width="24" aria-hidden="true" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM13.7071 9.70711C14.0976 9.31658 14.0976 8.68342 13.7071 8.29289C13.3166 7.90237 12.6834 7.90237 12.2929 8.29289L9.29289 11.2929C8.90237 11.6834 8.90237 12.3166 9.29289 12.7071L12.2929 15.7071C12.6834 16.0976 13.3166 16.0976 13.7071 15.7071C14.0976 15.3166 14.0976 14.6834 13.7071 14.2929L11.4142 12L13.7071 9.70711Z" fill="currentColor"/></svg> More articles
301</a>
302
303<script>
304((d) => {
305
306    // article anchors
307    Array.from(d.querySelectorAll('.article h2,.article h3,.article h4')).forEach((el) => {
308        const a = d.createElement('a')
309        a.href = `#${el.id}`;
310        a.setAttribute('aria-label', `The ${el.textContent} section`),
311        a.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>';
312        el.append(a);
313    });
314
315    // code controls
316    let copyId;
317    const updateCopyButton = (b, success) => {
318        b.dataset.state = success ? 'success' : success === false ? 'failure' : 'idle';
319        b.textContent = success ? 'Copied' : success === false ? 'Failed' : 'Copy';
320        clearTimeout(copyId);
321        copyId = setTimeout(() => updateCopyButton(b), 2500);
322    }
323    Array.from(d.querySelectorAll('pre')).forEach((el) => {
324
325        const buttons = d.createElement('div');
326        const lines = el.textContent.split(/^/gm).length;
327
328        if (lines < 20) {
329            const b = d.createElement('button');
330            b.className = 'code-share';
331            b.textContent = 'Snap picture';
332            b.type = 'button';
333            b.onclick = () => {
334                b.dataset.state = 'busy';
335                b.textContent = 'Generating picture';
336                import('/static/scripts/ui/CodePhotoModal.js').then(({ default: createCodePhotoModal }) => {
337                    createCodePhotoModal(el, {
338                        onReady: () => {
339                            b.dataset.state = 'success';
340                            b.textContent = 'Picture ready';
341                            setTimeout(() => {
342                                b.dataset.state = 'idle';
343                                b.textContent = 'Snap picture';
344                            }, 2500);
345                        },
346                        onError: (err) => {
347                            console.error(err);
348                            b.dataset.state = 'error';
349                            b.textContent = 'Failed to create picture';
350                        },
351                        onClose: () => {
352                            b.focus()
353                        }
354                    });
355                });
356            }
357            buttons.append(b);
358        }
359
360        if (navigator.clipboard) {
361            const b = d.createElement('button');
362            b.className = 'code-copy';
363            b.type = 'button';
364            b.textContent = 'Copy';
365            b.onclick = () => {
366                b.dataset.state = 'busy';
367                navigator.clipboard.writeText(el.querySelector('code').textContent).then(
368                    () => updateCopyButton(b, true),
369                    () => updateCopyButton(b, false)
370                );
371            }
372            buttons.append(b);
373        }
374
375        if (!buttons.childNodes.length) return;
376
377        el.prepend(buttons);
378    });
379
380    // newsletter
381    {
382        const setAttribute = (nodeList, name, value) => {
383            nodeList.forEach((el) => el.setAttribute(name, value));
384        };
385
386        const removeAttribute = (nodeList, name) => {
387            nodeList.forEach((el) => el.removeAttribute(name));
388        };
389
390        const minDelay = 1000;
391        
392        class AsyncForm extends HTMLElement {
393
394            connectedCallback() {
395
396                const form = this.querySelector('form');
397
398                const load = (status) => {
399                    const success = status >= 200 && status < 300;
400
401                    // no longer loading
402                    form.dataset.state = success ? 'success' : 'error';
403
404                    // unlock fields if was unsuccesful
405                    if (!success) {
406                        removeAttribute(
407                            form.querySelectorAll('input, textarea, button'),
408                            'disabled'
409                        );
410                    }
411                };
412
413                // handle submit
414                const submit = (e) => {
415                    // submit offset
416                    const offset = Date.now();
417
418                    // block normal submit
419                    e.preventDefault();
420
421                    // set loading state
422                    form.dataset.state = 'busy';
423
424                    // get data (before locking fields)
425                    var data = new FormData(form);
426
427                    // lock fields
428                    setAttribute(
429                        form.querySelectorAll('input, textarea, button'),
430                        'disabled',
431                        'disabled'
432                    );
433
434                    // do request
435                    const xhr = new XMLHttpRequest();
436                    xhr.onerror = () => {
437                        load();
438                    };
439                    xhr.onload = () => {
440                        const duration = Date.now() - offset;
441                        const delay = Math.max(0, minDelay - duration);
442                        setTimeout(() => {
443                            load(xhr.status);
444                        }, delay);
445                    };
446                    xhr.open(
447                        form.getAttribute('method') || 'POST',
448                        form.getAttribute('action')
449                    );
450                    xhr.send(data);
451                };
452
453                // wait for submit
454                form.addEventListener('submit', submit);
455            }
456        }
457
458        customElements.define("async-form", AsyncForm);
459    }
460    
461})(document);
462</script>
462
463    </main>
464
465    <footer>
466        <a href="/terms">Terms and conditions</a>
467        <a href="/privacy">Privacy policy</a>
468    </footer>
469
470    
471    <theme-switch>
472    <label for="theme-switch">Theme</label>
473    <input type="checkbox" value="1" id="theme-switch" hidden/>
474    <label for="theme-switch" aria-label="Toggle theme">
475        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="moon" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
476        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="sun" aria-hidden="true"><circle cx="12" cy="12" r="5"/><g stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></g></svg>
477    </label>
478</theme-switch>
479<script>
480class ThemeSwitch extends HTMLElement {
481  constructor() {
482    super();
483
484    const KEY = 'theme';
485
486    const checkbox = this.querySelector('input');
487    if (!checkbox) return;
488
489    const setTheme = (theme) => {
490        localStorage.setItem(KEY, theme);
491        document.documentElement.dataset.theme = theme;
492    }
493
494    const redraw = () => setTheme(checkbox.checked ? 'dark' : 'bright');
495
496    if (document.documentElement.dataset.theme === 'dark') checkbox.checked = true;
497
498    checkbox.addEventListener('change', redraw);
499
500    redraw();
501  }
502}
503customElements.define('theme-switch', ThemeSwitch);
504</script>
504
505
506    
507    
508    
508<script async defer src="https://cdn.simpleanalytics.io/hello.js"></script>
508
509    <noscript><img src="https://api.simpleanalytics.io/hello.gif" alt="sa" aria-hidden="true"></noscript>
510    
511
512    
513    <a rel="me" href="https://mastodon.social/@rikschennink" style="display:none"></a>
514
515<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"77f1dceb16c345ba8ea707ffe766b763","r":1,"spa":2}' crossorigin="anonymous"></script>
515
516</body>
517</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.