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.