<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>web.dev</title>
  <subtitle>Let&#39;s build the future of the web.</subtitle>
  <link href="https://web.dev/feed.xml" rel="self"/>
  <link href="https://web.dev"/>
  <updated>2018-11-04T16:00:00-08:00</updated>
  <id>https://web.dev/</id>
  <author>
    <name>Google Developers</name>
  </author>
  
  <entry>
    <title>Ready Player Web</title>
    <link href="https://web.dev/ready-player-web/"/>
    <updated>2019-08-20T17:00:00-07:00</updated>
    <id>https://web.dev/ready-player-web/</id>
    <content type="html">&lt;p&gt;Good game developers know that to capitalise on the opportunity of a particular platform it&#39;s important to embrace the unique characteristics of that platform. So what are the unique characteristics of the web? And what defines a web game?&lt;/p&gt;
&lt;p&gt;At Google I/O 2019 I presented my thoughts on the state of the web games ecosystem, the current best practices for modern web game development, and where the industry is heading. In this blog post, I&#39;ll summarise some of the key points from my talk which you can watch in full on YouTube:&lt;/p&gt;
&lt;div class=&quot;w-youtube&quot;&gt;
  &lt;iframe class=&quot;w-youtube__embed&quot; src=&quot;https://www.youtube.com/embed/aVTYxHL45SA&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;the-challenges-of-web-games&quot;&gt;The challenges of web games &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-challenges-of-web-games&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before joining Google I created a mobile game known as &lt;a href=&quot;https://www.duetgame.com/&quot;&gt;Duet&lt;/a&gt; which was downloaded nearly 20 million times. Through that experience I learned that the three essential ingredients to building a successful business out of a game are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A functional game&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;li&gt;A way to monetize users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without these three elements, a game developer cannot succeed. Nowadays, these last two points are the most critical. Closed HTML5 ecosystems such as WeChat, Facebook Instant Games, and more have demonstrated that building games using HTML5 is achievable.&lt;/p&gt;
&lt;h2 id=&quot;modern-best-practices&quot;&gt;Modern best practices &lt;a class=&quot;w-headline-link&quot; href=&quot;#modern-best-practices&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By &amp;quot;functional game&amp;quot; I refer to the three most core elements of what makes a game work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Visuals&lt;/li&gt;
&lt;li&gt;Audio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each of these areas, the web platform has made significant strides in the past few years. For CPU performance we have access to a &lt;a href=&quot;https://www.youtube.com/watch?v=njt-Qzw0mVY&quot;&gt;performant new standard called Web Assembly&lt;/a&gt;. From the graphics side, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL 1.0&lt;/a&gt; has &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API#WebGL_1&quot;&gt;good cross-browser support&lt;/a&gt; and future standards such as &lt;a href=&quot;https://www.youtube.com/watch?v=K2JzIUIHIhc&quot;&gt;WebGPU&lt;/a&gt; are positioning the web platform for an extensible future of graphics programming similar to Vulkan and Metal. Finally, for web audio we have the &lt;a href=&quot;https://www.youtube.com/watch?v=-GaD0RCp-Q0&quot;&gt;common Web Audio API and more recently the Audio Worklet API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently, Unity previewed a new runtime called Project Tiny which is focused on building 2D games for HTML5-based platforms. Project Tiny applies a new modular design to the engine structure of Unity enabling the core Unity engine to be under 1 megabyte in size.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img src=&quot;https://web.dev/ready-player-web/unity-tanks.gif&quot; alt=&quot;Two tanks engaged in a battle.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Unity&#39;s Tanks Demo exported via HTML5.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;From the technical side, there has never been a better time to embrace web game development.&lt;/p&gt;
&lt;h2 id=&quot;enter-the-loop&quot;&gt;Enter the loop &lt;a class=&quot;w-headline-link&quot; href=&quot;#enter-the-loop&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A great game is obviously more than just good performance, graphics, and sound though–to be great a game must be fun.&lt;/p&gt;
&lt;p&gt;Fun is a difficult element to measure in a product. When a game is fun, interesting, or innovative enough, users will want to tell their friends–in other words, they&#39;ll want to share the experience. Tapping into this opportunity and coupling it with the web is a powerful combination that unlocks a lot of potential for viral growth. And on the web in particular, without a central discovery platform, our best bet towards acquiring users is to ensure our games are as viral as possible.&lt;/p&gt;
&lt;p&gt;Good game developers know that to capitalise on a particular platform–whether at a software or hardware level–it&#39;s important to embrace the unique characteristics of that platform. For example, if you&#39;re building a game for a console with motion controls, you should probably think about the best way to embrace those motion controls.&lt;/p&gt;
&lt;p&gt;In other words, you must respect the expectations of the users of the platform you&#39;re building for. What do users of the web expect? They expect web content to load fast and be interactive quickly. In my talk, I covered several examples of ways–both on and off the web–that games have been designed to load quickly, pull users into their game worlds, engage those users, and provide users with additional incentives to share their experiences.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/ready-player-web/minimalist-games.png&quot; alt=&quot;Three games with minimalist art styles.&quot;&gt;&lt;/p&gt;
&lt;p&gt;I personally believe that the key to building a successful web game is to lean into this unique characteristic of the web. Specifically, the strength of the web&#39;s URL structure and the sharing loop that users can join in.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of a web game I built using &lt;a href=&quot;http://construct.net/&quot;&gt;Construct 3&lt;/a&gt; that leverages the URL in a fun and engaging way.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/ready-player-web/space-board.png&quot; alt=&quot;A level editor interface for a game.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://io-space-board.firebaseapp.com/&quot;&gt;Space Board&lt;/a&gt; is a very simple game that can be played on either mobile with touch controls or on desktop with keyboard input. The objective is to navigate a maze of obstacles to reach a goal at the end.&lt;/p&gt;
&lt;p&gt;How does Space Board leverage the URL in a unique fashion? By encoding the level structure into the URL itself. All levels are defined as a 10 by 10 grid of objects–e.g. walls, enemy turrets, keys, locked doors etc. The URL then lists all the individual grid positions and their contents. A wall is represented by a &lt;code&gt;W&lt;/code&gt; character. An empty space is an underscore character.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;https://io-space-board.firebaseapp.com/?gameWorld=_wwwwwwwwww___ww__eww_k__d___ww___ww___ww_wwwww_www_wwwww_www___ww___ww_s_ww_f_ww___ww___wwwwwwwwwwww&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s ugly but it does the job.&lt;/p&gt;
&lt;p&gt;Upon completing a level in Space Board, the player has the opportunity to design their own level using the simple level editor shown above. By enabling players to design their own levels we are giving them the opportunity for personalisation. When a user feels a connection to a game and a sense of ownership via creation and customisation they are more likely to want to share that &#39;thing&#39; with the world.&lt;/p&gt;
&lt;p&gt;The desire to share a game is the beginning of the viral loop that we are aiming to achieve with our web games. This game design and sharing mechanism is just one example that&#39;s possible but there are many other possibilities–I encourage you to watch my talk for further examples!&lt;/p&gt;
&lt;h2 id=&quot;return-on-investment&quot;&gt;Return on investment &lt;a class=&quot;w-headline-link&quot; href=&quot;#return-on-investment&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At present, there are ultimately two schools of thought with regards to how a game developer can generate revenue through web games:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monetizing the games directly&lt;/li&gt;
&lt;li&gt;Treating them as an acquisition channel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Treating web games as an acquisition channel means leveraging the web version of your native game as a mechanism to get your players hooked and convincing them to download your larger native binary. You then generate revenue with the native platform&#39;s built-in payment and billing backends.&lt;/p&gt;
&lt;p&gt;Monetization is usually a mixture of advertising and microtransactions. There is still work to be done for the web to compete with native mobile platforms in game advertising. For example, formats like Rewarded Video Ads have been extremely popular for native mobile games for several years and yet we&#39;re only now seeing ad networks deploy these formats on the web.&lt;/p&gt;
&lt;p&gt;Nonetheless, there are game developers who continue to be successful on the open web through advertising via traditional banner ads and interstitial video ads. Take a look at &lt;a href=&quot;https://support.google.com/adsense/answer/1705831&quot;&gt;Adsense for Games&lt;/a&gt; for more information on these formats.&lt;/p&gt;
&lt;p&gt;For microtransactions, the web offers complete flexibility due to the limitless number of payment methods that can be implemented. However this quality is a double-edged sword. The negative side of this is that players have less implicit trust towards a new website they discover versus the familiarity of the native mobile store payment methods.&lt;/p&gt;
&lt;p&gt;One solution that brings a more consistent payment UI to the web is the &lt;a href=&quot;https://developers.google.com/web/fundamentals/payments/&quot;&gt;Payment Request API&lt;/a&gt;. This API invokes a UI that is shown by the browser and streamlines the acquisition of payment details such as credit cards and billing addresses. However, acquiring payment details is just the first step of making a transaction. You need a backend billing platform as well.&lt;/p&gt;
&lt;h2 id=&quot;the-future&quot;&gt;The future &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-future&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&#39;ve seen several surprisingly successful web games over the past few years. Slither.io has built a mixed web and native business that demonstrates the tremendous reach and viral growth opportunity that the web offers. Portals such as Poki.com are innovating in their user experience and releasing new games every month that match the fidelity of their mobile counterparts, such as Crossy Road.&lt;/p&gt;
&lt;p&gt;Furthermore, if you look outside of the open web you can see that web games are already taking off. Closed ecosystems such as WeChat and LINE offer satisfying games which aren&#39;t playable on the open web but which are built on top of web technologies like HTML5 and WebViews. This is a clear sign that the web has reached a level of fidelity that&#39;s capable of rivaling native mobile games–perhaps not in a textbook definition of fidelity but in a more important metric: player attention.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Progressive Web Apps in multi-origin sites</title>
    <link href="https://web.dev/multi-origin-pwas/"/>
    <updated>2019-08-18T17:00:00-07:00</updated>
    <id>https://web.dev/multi-origin-pwas/</id>
    <content type="html">&lt;h2 id=&quot;background&quot;&gt;Background &lt;a class=&quot;w-headline-link&quot; href=&quot;#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the past, there were some advantages to using multi-origin architectures, but for Progressive Web Apps, that approach presents many challenges. In particular, the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy&quot;&gt;same-origin policy&lt;/a&gt;, imposes restrictions for sharing things like service workers and caches, permissions, and for achieving a standalone experience across multiple origins. This article will describe the good and bad uses of multiple origins, and explain the challenges and workarounds for building Progressive Web Apps in multi-origin sites.&lt;/p&gt;
&lt;h2 id=&quot;good-and-bad-uses-of-multiple-origins&quot;&gt;Good and bad uses of multiple origins &lt;a class=&quot;w-headline-link&quot; href=&quot;#good-and-bad-uses-of-multiple-origins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a few legitimate reasons for sites to employ a multi-origin architecture, mostly related to providing an independent set of web applications, or to create experiences that are completely isolated from each other. There are also uses that should be avoided.&lt;/p&gt;
&lt;h3 id=&quot;the-good&quot;&gt;The good &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-good&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s look at the useful reasons first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Localization / Language:&lt;/strong&gt; Using a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/TLD&quot;&gt;country-code top-level domain&lt;/a&gt;, to separate sites to be served in different countries (e.g. &lt;code&gt;https://www.google.com.ar&lt;/code&gt;), or using subdomains to divide sites targeted to different locations (e.g.: &lt;code&gt;https://newyork.craigslist.org&lt;/code&gt;) or to offer content for a specific language (e.g. &lt;code&gt;https://en.wikipedia.org&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Device/Platform:&lt;/strong&gt; Using different subdomains, to deliver different versions of a website to different devices. The &lt;code&gt;m.&lt;/code&gt; or &lt;code&gt;mobile.&lt;/code&gt; pattern is a common way to separate mobile from desktop in adaptive sites. For example: a site can maintain its desktop version at &lt;code&gt;https://www.example.com&lt;/code&gt; and mobile version at &lt;code&gt;https://m.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Independent webapps:&lt;/strong&gt; Using different subdomains to provide experiences whose purpose differs considerably from the site on the main origin. For example, in a news site, the crosswords webapp could be intentionally served from &lt;code&gt;https://crosswords.example.com&lt;/code&gt;, and installed and used as an independent PWA, without having to share any resources or functionality with the main website.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-bad&quot;&gt;The bad &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-bad&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you&#39;re not doing any of these things, it&#39;s likely that using a multi-origin architecture will be a disadvantage when building Progressive Web Apps.&lt;/p&gt;
&lt;p&gt;Despite this, many sites continue being structured this way for no particular reason, or for &#39;legacy&#39; reasons. One example is using subdomains to arbitrarily separate parts of a site that should be part of a unified experience.&lt;/p&gt;
&lt;p&gt;The following patterns, for example, are highly discouraged:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Site sections:&lt;/strong&gt; Separating different sections of a site on subdomains. In news sites, it&#39;s not uncommon to see the home page at: &lt;code&gt;https://www.example.com&lt;/code&gt;, while the sports section lives at &lt;code&gt;https://sports.example.com&lt;/code&gt;, politics at &lt;code&gt;https://politics.example.com&lt;/code&gt;, and so forth. In the case of an e-commerce site, using something like &lt;code&gt;https://category.example.com&lt;/code&gt; for product categories, &lt;code&gt;https://product.example.com&lt;/code&gt; for product pages, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Flow:&lt;/strong&gt; Another approach that&#39;s discouraged is to separate different smaller parts of the site, like pages for the login or purchase flows in subdomains. For example, using &lt;code&gt;https://login.example.com&lt;/code&gt;, and &lt;code&gt;https://checkout.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;When building a site from scratch it&#39;s highly recommended to avoid dividing it into subdomains. For existing sites, migrating to a single origin is the best approach.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For those cases where migrating to a single origin is not possible, what follows is a list of challenges, and (where possible), workarounds that can be considered when building Progressive Web Apps.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-workarounds-for-pwas-across-different-origins&quot;&gt;Challenges and Workarounds for PWAs across different origins &lt;a class=&quot;w-headline-link&quot; href=&quot;#challenges-and-workarounds-for-pwas-across-different-origins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When building a website on multiple origins, providing a unified PWA experience is challenging, mostly because of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy&quot;&gt;same-origin policy&lt;/a&gt;, which imposes a number of constraints. Let&#39;s look at them one at a time.&lt;/p&gt;
&lt;h3 id=&quot;service-workers&quot;&gt;Service workers &lt;a class=&quot;w-headline-link&quot; href=&quot;#service-workers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The origin of the service worker script URL has to be the same as the origin of the page calling &lt;a href=&quot;https://w3c.github.io/ServiceWorker/#navigator-service-worker-register&quot;&gt;register()&lt;/a&gt;. This means that, for example, a page at &lt;code&gt;https://www.example.com&lt;/code&gt; can&#39;t call &lt;code&gt;register()&lt;/code&gt; with a service worker url at &lt;code&gt;https://section.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another consideration is that a service worker can only control pages hosted under the origin and path it belongs to. This means that, if the service worker is hosted at &lt;code&gt;https://www.example.com&lt;/code&gt; it can only control URLs from that origin (according to the path defined in the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#Parameters&quot;&gt;scope parameter&lt;/a&gt;), but won&#39;t control any page in other subdomains such as, for example, those in &lt;code&gt;https://section.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this case, the only workaround is to use multiple service workers (one per origin).&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
Registering, and having multiple active service workers consumes additional resources (memory, CPU, etc.), so use your best judgement on how many active service workers a user will likely need to navigate across the site.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;caching&quot;&gt;Caching &lt;a class=&quot;w-headline-link&quot; href=&quot;#caching&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Cache object, indexedDB, and localStorage are also constrained to a single origin. This means it&#39;s not possible to access the caches that belong to &lt;code&gt;https://www.example.com&lt;/code&gt;, from, for example: &lt;code&gt;https://www.section.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here are some things you can do to manage caches properly in scenarios like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Leverage browser caching:&lt;/strong&gt; Using &lt;a href=&quot;https://webkit.org/blog/8090/workers-at-your-service/&quot;&gt;traditional browser caching best practices&lt;/a&gt; is always recommended. This technique provides the added benefit of reusing cached resources across origins, which can&#39;t be done with the service worker&#39;s cache. For best practices on how to use HTTP Cache with service workers, you can take a look at &lt;a href=&quot;https://jakearchibald.com/2016/caching-best-practices/#the-service-worker-the-http-cache-play-well-together-dont-make-them-fight&quot;&gt;this post&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep service worker installation lightweight:&lt;/strong&gt; If you are maintaining multiple service workers, avoid making users pay a big installation cost every time they navigate to a new origin. In other words: only pre-cache resources that are absolutely necessary.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;w-aside w-aside--gotchas&quot;&gt;
&lt;strong&gt;Gotchas!&lt;/strong&gt; &lt;p&gt;Once the service worker is active and running, the same-origin policy also restricts cross-origin requests made &lt;strong&gt;&lt;em&gt;inside&lt;/em&gt;&lt;/strong&gt; service workers. Fortunately this has a recommended workaround, which is to use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS&quot;&gt;CORS&lt;/a&gt; (as explained &lt;a href=&quot;https://developers.google.com/web/ilt/pwa/working-with-the-fetch-api#cross-origin_requests&quot;&gt;here&lt;/a&gt;). Using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters&quot;&gt;no-cors mode&lt;/a&gt; when fetching resources inside the service worker is not recommended.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;permissions&quot;&gt;Permissions &lt;a class=&quot;w-headline-link&quot; href=&quot;#permissions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Permissions are also scoped to origins. This means that if a user granted a given permission to the origin &lt;code&gt;https://maps.google.com&lt;/code&gt;, it won&#39;t carry over to other origins, like &lt;code&gt;https://www.google.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since there&#39;s no way to share permissions across origins, the only solution here is to ask for permission on each of subdomain where a given feature is required (e.g. location). For things like web push, you can maintain a cookie to track if the permission has been accepted by the user in another subdomain, to avoid requesting it again.&lt;/p&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation &lt;a class=&quot;w-headline-link&quot; href=&quot;#installation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To install a PWA, each origin must have its own manifest with a &lt;code&gt;start_url&lt;/code&gt; that&#39;s &lt;a href=&quot;https://w3c.github.io/manifest/#start_url-member&quot;&gt;relative to itself&lt;/a&gt;. This means that a user receiving the installation prompt on a given origin (i.e: &lt;code&gt;https://section.example.com&lt;/code&gt;) won&#39;t be able to install the PWA with a &lt;code&gt;start_url&lt;/code&gt; on a different one (i.e: &lt;code&gt;https://www.example.com&lt;/code&gt;).
In other words, users receiving the installation prompt in a subdomain will only be able to install PWAs for the subpages, not for the main URL of the app.&lt;/p&gt;
&lt;p&gt;A simple workaround is to use a&lt;code&gt;start_url&lt;/code&gt; with a redirect to the main origin. For example: subdomain &lt;code&gt;https://section.example.com&lt;/code&gt;, can define a &lt;code&gt;start_url&lt;/code&gt; of &lt;code&gt;https://section.example.com/pwa&lt;/code&gt;, containing a redirect to: &lt;code&gt;https://www.example.com&lt;/code&gt;, making the user always land at the home page.&lt;/p&gt;
&lt;p&gt;There&#39;s also the issue that the same user could receive multiple installation prompts when navigating the site, if each subdomain meets the &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#criteria&quot;&gt;installation criteria&lt;/a&gt;, and prompts the user to install the PWA.&lt;/p&gt;
&lt;p&gt;Mitigate this problem by applying any of the following techniques:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Showing the prompt on a single domain:&lt;/strong&gt; If the site has many subdomains that pass the installation criteria, the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event could be used and &lt;code&gt;preventDefault()&lt;/code&gt; called (as explained &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#listen_for_beforeinstallprompt&quot;&gt;here&lt;/a&gt;), to prevent the prompt from appearing in unintended parts of the site (like subdomains), while continue to show it in other parts (e.g. the home page).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Showing the prompt once, across different subdomains:&lt;/strong&gt; It&#39;s also possible to let each subdomain show the installation prompt. (This can make sense if some subdomains are more frequently visited). In this case, it&#39;s important to avoid showing the prompt to a user multiple times. To that purpose, a cookie could be used, to track if the prompt has already been shown, and check for its existence when the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event is triggered. If the cookie is present, it means that the user already received the prompt somewhere else, so &lt;code&gt;preventDefault()&lt;/code&gt; can be called, to avoid showing it again.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;standalone-mode&quot;&gt;Standalone Mode &lt;a class=&quot;w-headline-link&quot; href=&quot;#standalone-mode&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While navigating in a standalone window, the browser will behave differently when the user moves outside of the scope set by the PWA&#39;s manifest. The behavior depends on each browser version and vendor. For example, the latest Chrome versions open a &lt;a href=&quot;https://developer.chrome.com/multidevice/android/customtabs&quot;&gt;Chrome custom tab&lt;/a&gt;, when a user moves out of the scope in fullscreen mode.&lt;/p&gt;
&lt;p&gt;In most cases, there&#39;s no solution for this, but a workaround can be applied for small parts of the experience that are hosted in subdomains (for example: login workflows):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The new URL, &lt;code&gt;https://login.example.com&lt;/code&gt;, could open inside a full screen iframe.&lt;/li&gt;
&lt;li&gt;Once the task is completed inside the iframe (for example, the login process), &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage&quot;&gt;postMessage()&lt;/a&gt; can be used, to pass any resulting information from the iframe back to the parent page.&lt;/li&gt;
&lt;li&gt;As a final step, once the message is received by the main page, the listeners can be unregistered, and the iframe finally be removed from the DOM.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
The previous technique can help mitigating the potential UI change in a small part of the site, where the user can perform an action in a subdomain and return to the main origin (like in a login flow), but won&#39;t be an efficient technique to implement for entire paths, including many pages hosted in subdomains (like entire site sections).&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Same-origin policy imposes many restrictions for sites built on top of multiple origins that want to achieve a coherent PWA experience. For that reason, to provide the best experience to users, we strongly recommend against dividing sites into different origins.&lt;/p&gt;
&lt;p&gt;For existing sites that are already built in this way, it can be challenging to make multi-origin PWAs work correctly, but we have explored some potential workarounds. Each can come with tradeoffs, so use your judgement when deciding which approach to take on your website.&lt;/p&gt;
&lt;p&gt;When evaluating a long-term strategy or site redesign, consider migrating to a single origin, unless there&#39;s an important reason to keep the multi-origin architecture.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;With many thanks for their technical reviews and suggestions: PJ Mclachlan, Paul Covell, Dominick Ng, Alberto Medina, Pete LePage, Joe Medley, Cheney Tsai, Martin Schierle, and Andre Bandarra.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How Truebil made the web its channel of growth</title>
    <link href="https://web.dev/truebil-lite/"/>
    <updated>2019-08-15T17:00:00-07:00</updated>
    <id>https://web.dev/truebil-lite/</id>
    <content type="html">&lt;h2 id=&quot;about&quot;&gt;About &lt;a class=&quot;w-headline-link&quot; href=&quot;#about&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Founded in 2015, Truebil is an Indian online marketplace that sells 100% certified used cars. With over 1.4 million monthly active users, it&#39;s a one-stop solution that includes title transfer, insurance, loans, and service warranties. Prospective customers can see individual product pages with images and detailed inspection reports and get vehicle evaluations with the site&#39;s &amp;quot;Compare&amp;quot; and &amp;quot;Truescore&amp;quot; features. Truebil differentiates its product with rich features, including personalized recommendations based on machine learning, an add-to-favorites feature, a share-a-car feature, and more.&lt;/p&gt;
&lt;h2 id=&quot;challenge&quot;&gt;Challenge &lt;a class=&quot;w-headline-link&quot; href=&quot;#challenge&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Truebil is a lean startup with low-frequency, high-value transactions, so it was critical to choose the right platform to prioritize and invest in.&lt;/p&gt;
&lt;p&gt;Truebil identified mobile as their target platform, and they chose the web for their first app, &lt;a href=&quot;https://m.truebil.com/&quot;&gt;Truebil Lite&lt;/a&gt;, because of the web&#39;s easy discovery and low friction. Web technology provides lower development costs, less data and memory usage, and significantly lower customer acquisition costs than building a native app. And by building a progressive web app (PWA), Truebil could get all the perks of the web &lt;em&gt;and&lt;/em&gt; the benefits of native.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot;&gt;Solution &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An in-house team took four months to develop Truebil Lite using React, Django, and Preact (for production migration). They set clear guiding principles for the web app based on user goals. The experience had to be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt; on first load and subsequent navigations,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliable&lt;/strong&gt;, independent of the user&#39;s network or device constraints, and&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engaging&lt;/strong&gt;, especially for small mobile screens, so users would want to return to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;optimize-for-fast-first-load-and-navigations&quot;&gt;Optimize for fast first load and navigations &lt;a class=&quot;w-headline-link&quot; href=&quot;#optimize-for-fast-first-load-and-navigations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; to guide performance optimizations, the team adopted a performance-first culture while implementing new features. Truebil was able to significantly improve user experience by prioritizing the &lt;a href=&quot;https://web.dev/first-contentful-paint&quot;&gt;First Contentful Paint&lt;/a&gt; and &lt;a href=&quot;https://web.dev/interactive&quot;&gt;Time to Interactive (TTI)&lt;/a&gt; metrics and optimizing for fast first loads, repeat visits, and smooth navigation. The team achieved those results by setting performance budgets and using a variety of techniques to achieve them.&lt;/p&gt;
&lt;h4 id=&quot;set-performance-budgets&quot;&gt;Set performance budgets &lt;a class=&quot;w-headline-link&quot; href=&quot;#set-performance-budgets&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;With a performance-first mindset, the Truebil team chose to architect their experience as a single page app with server-side rendering for first load and client-side rendering for subsequent loads. Keeping web apps with client side rendering performant can be difficult, so Truebil set very strict &lt;a href=&quot;https://web.dev/fast#set-performance-budgets&quot;&gt;performance budgets&lt;/a&gt; to ensure they don&#39;t compromise on speed, especially as they add more features.&lt;/p&gt;
&lt;p&gt;The team set strict milestone-based budgets for TTI with the goal of keeping it below five seconds. To meet that goal they manually ensured no build would exceed a 250 KB JavaScript bundle size, kept a constant check on image sizes, and continually tracked the app&#39;s Lighthouse performance score.&lt;/p&gt;
&lt;h4 id=&quot;optimize-javascript-bundles&quot;&gt;Optimize JavaScript bundles &lt;a class=&quot;w-headline-link&quot; href=&quot;#optimize-javascript-bundles&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The team started with the basics by using the &lt;a href=&quot;https://web.dev/apply-instant-loading-with-prpl&quot;&gt;PRPL pattern&lt;/a&gt; to precache and optimize JavaScript payloads and by moving to HTTP/2 to serve critical JavaScript bundles.&lt;/p&gt;
&lt;p&gt;To lazy-load non-critical resources, they used their framework-level lazy-loading components to load below-the-fold fragments.&lt;/p&gt;
&lt;p&gt;To remove any JavaScript bundle bottlenecks, the team &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting&quot;&gt;reduced payloads by code-splitting&lt;/a&gt;. They used component- and route-based chunking to to reduce main bundle size and &lt;strong&gt;improve their loading time by 44%,&lt;/strong&gt; with TTI falling from 6 seconds to about 5 seconds and &lt;a href=&quot;https://web.dev/first-meaningful-paint&quot;&gt;First Meaningful Paint (FMP)&lt;/a&gt; from 4.1 seconds to 3.6 seconds.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/truebil-lite/chunking.png&quot; alt=&quot;Screenshots of Chrome DevTools showing Truebil Lite&#39;s build size before and after code splitting.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Impact of reducing chunk size.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;inline-critical-css&quot;&gt;Inline critical CSS &lt;a class=&quot;w-headline-link&quot; href=&quot;#inline-critical-css&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To further improve FMP, the team used Lighthouse to find opportunities for and validate the impact of performance optimizations. Lighthouse indicated that reducing render blocking CSS would have the biggest effect, so Truebil inlined all critical CSS and &lt;a href=&quot;https://web.dev/defer-non-critical-css&quot;&gt;deferred non-critical CSS&lt;/a&gt;. This technique &lt;strong&gt;reduced FMP by around 2 seconds&lt;/strong&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/truebil-lite/first-meaningful-paint.png&quot; alt=&quot;Screenshots of Chrome DevTools showing Truebil Lite&#39;s time to First Meaningful Paint before and after inlining CSS.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Impact of inlining critical CSS.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;avoid-multiple-costly-round-trips-to-any-origin&quot;&gt;Avoid multiple, costly round trips to any origin &lt;a class=&quot;w-headline-link&quot; href=&quot;#avoid-multiple-costly-round-trips-to-any-origin&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To mitigate overhead from DNS and TLS, Truebil used &lt;a href=&quot;https://web.dev/uses-rel-preconnect&quot;&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;preconnect&amp;quot;&amp;gt;&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;&amp;lt;link rel=&amp;quot;dns-prefetch&amp;quot;&amp;gt;&lt;/code&gt;. This approach causes the browser to complete the TLS handshake as soon as possible on page load and pre-resolve cross-origin domain names, allowing for a secure, snappy user experience.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/truebil-lite/preconnect.png&quot; alt=&quot;Screenshots of Chrome DevTools showing the effect of rel=preconnect.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Impact of adding &lt;code&gt;&amp;#60;link rel=preconnect&amp;#62;&lt;/code&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;dynamically-prefetch-the-next-page&quot;&gt;Dynamically prefetch the next page &lt;a class=&quot;w-headline-link&quot; href=&quot;#dynamically-prefetch-the-next-page&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;By analyzing their data, the team identified the most common user journeys that they could optimize for. In these cases, the app dynamically downloads the next page resource by using &lt;code&gt;&amp;lt;link rel=prefetch&amp;gt;&lt;/code&gt; to ensure smooth navigation for users. While the team manually identifies the links to prefetch, they use webpack to bundle the JS for those links.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/truebil-lite/prefetch.png&quot; alt=&quot;Screenshots of the Truebil Lit app and Chrome DevTools showing that network requests aren&#39;t needed on common navigations because the assets have already been prefetched.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    The effect of prefetching assets for common user journeys.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;optimize-images-and-fonts&quot;&gt;Optimize images and fonts &lt;a class=&quot;w-headline-link&quot; href=&quot;#optimize-images-and-fonts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Images are a critical part of Truebil&#39;s product experience and credibility, with each product listing including up to 40 pictures. To ensure that images do not block page load, the team chose to &lt;a href=&quot;https://web.dev/image-cdns&quot;&gt;serve all their resources from a CDN&lt;/a&gt; and use &lt;a href=&quot;https://imagemagick.org/index.php&quot;&gt;imagemagick&lt;/a&gt; for image optimization. They also Gzipped all compressible resources, including images, JavaScript, and CSS, to further cut down load time.&lt;/p&gt;
&lt;p&gt;To &lt;a href=&quot;https://web.dev/avoid-invisible-text&quot;&gt;avoid a flash of invisible text&lt;/a&gt; while keeping load time as low as possible, Truebil set up their CSS to use system fonts as a fallback until external fonts have loaded.&lt;/p&gt;
&lt;h4 id=&quot;further-optimizations&quot;&gt;Further optimizations &lt;a class=&quot;w-headline-link&quot; href=&quot;#further-optimizations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When the app was ready, the team wanted to further reduce the vendor bundle size and JavaScript execution time, so they switched their React app to Preact in production. (Learn more in the &lt;a href=&quot;https://web.dev/react&quot;&gt;React&lt;/a&gt; collection.) This approach helped them reduce the vendor bundle size from 82.3 KB to 51.2 KB.&lt;/p&gt;
&lt;h3 id=&quot;build-in-reliability&quot;&gt;Build in reliability &lt;a class=&quot;w-headline-link&quot; href=&quot;#build-in-reliability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With a focus on the Indian market, a vast majority of Truebil&#39;s users access their product on patchy networks that sometimes fall into bandwidths as low as 2G. So building a resilient experience was critical not only to improving performance under constrained network conditions but also to delivering a product that their users could rely on—one that &lt;em&gt;always&lt;/em&gt; works.&lt;/p&gt;
&lt;h4 id=&quot;a-hybrid-caching-strategy-for-reliable-loading&quot;&gt;A hybrid caching strategy for reliable loading &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-hybrid-caching-strategy-for-reliable-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The interactivity and rate of change for Truebil&#39;s content vary a lot. To ensure that &lt;em&gt;all&lt;/em&gt; its content is both fresh and reliable, the Truebil team implemented &lt;a href=&quot;https://web.dev/runtime-caching-with-workbox&quot;&gt;API caching&lt;/a&gt; using a combination of network-first, cache-first, and fastest-first strategies.&lt;/p&gt;
&lt;p&gt;For static pages, such as the subscriptions page, Truebil uses a cache-first strategy to go to their subscription API cache first, falling back to the network.&lt;/p&gt;
&lt;p&gt;For pages with dynamic content that rarely changes, such as their product listing or details pages, Truebil uses a network-first strategy so that the browser first checks the network for content before falling back to the API cache if the network is unavailable.&lt;/p&gt;
&lt;p&gt;And for dynamic pages that change often, such as the home, filter, search, and city pages, Truebil uses a fastest-first strategy to choose between network or cache based on whichever comes first. To ensure that content is fresh, the cache is updated whenever the network response differs from what&#39;s in the cache.&lt;/p&gt;
&lt;h4 id=&quot;service-workers-for-a-full-offline-experience&quot;&gt;Service workers for a full offline experience &lt;a class=&quot;w-headline-link&quot; href=&quot;#service-workers-for-a-full-offline-experience&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Even though a large part of Truebil&#39;s content is highly dynamic—cars can be added or bought at any time—the team wanted to ensure that their users had &lt;em&gt;some&lt;/em&gt; content to engage with, even if they were going through patchy networks or were completely offline.&lt;/p&gt;
&lt;p&gt;Using &lt;a href=&quot;https://web.dev/service-workers-cache-storage/&quot;&gt;service workers&lt;/a&gt;, the team was able to cache both static data and the dynamic data that a user has already interacted with so that the user can view it offline. To make sure users know that content might change when they come back online, the team changed the UI to grayscale to indicate offline mode. Browsing product pages is a critical part of the Truebil user journey. Users who have visited the PWA at least once can browse listings and product pages that they have visited before but won&#39;t be able to see any updates to the listing or the product.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img src=&quot;https://web.dev/truebil-lite/grayscale.png&quot; alt=&quot;A screenshot of the Truebil Lite app in offline mode.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Truebil Lite in offline mode.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;improve-engagement-to-keep-users-coming-back&quot;&gt;Improve engagement to keep users coming back &lt;a class=&quot;w-headline-link&quot; href=&quot;#improve-engagement-to-keep-users-coming-back&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;an-engaging-first-experience&quot;&gt;An engaging first experience &lt;a class=&quot;w-headline-link&quot; href=&quot;#an-engaging-first-experience&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since most of their users come from paid channels, Truebil needed to supplement their fast loading web app with a product that surfaces highly relevant recommendations to increase conversions. While the team uses a recommendation system based on sophisticated filtering for existing users, their system doesn&#39;t work for users who log in for the first time.&lt;/p&gt;
&lt;p&gt;To avoid giving their first-time users a cold start, the team integrated a recommendation system using their digital marketing efforts. They add product details such as car model, price, and body type into an ad&#39;s destination URL through a UTM parameter, which is read by their recommendation system and reflected in the products surfaced. In case the sysme reads no such details in the URL, it falls back to popular cars, which is a combination of popular models, popular budgets, and cars that have been popular in the last few weeks or days.&lt;/p&gt;
&lt;h4 id=&quot;an-installable-web-app&quot;&gt;An installable web app &lt;a class=&quot;w-headline-link&quot; href=&quot;#an-installable-web-app&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Having built a fast, full-featured web app with a compelling user experience, Truebil wanted to ensure that their users would keep coming back. They realized that making the app installable would make repeat visits much more seamless.&lt;/p&gt;
&lt;p&gt;The team implemented the &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/&quot;&gt;Add to Home Screen&lt;/a&gt; feature to make their product a full progressive web app (PWA). This approach allowed users to add Truebil Lite to the home screen and launch it in full-screen mode. And since they had already implemented an offline mode, the team was able to add the new feature easily.&lt;/p&gt;
&lt;p&gt;To ensure that their users weren&#39;t spammed and to increase the probability that users would install the app, the team recently updated their strategy for &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/promoting-install-mobile&quot;&gt;promoting PWA installation&lt;/a&gt; so that  installation prompts appear when they&#39;ll actually be useful to different kinds of users. Truebil settled on a three-part strategy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show prompts when the user has completed an action or is idle.&lt;/li&gt;
&lt;li&gt;Show contextual prompts to mature users.&lt;/li&gt;
&lt;li&gt;Show a banner when the user has spent a set amount of time on the site.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;default-banners-on-process-completion-and-on-high-traffic-pages&quot;&gt;Default banners on process completion and on high-traffic pages &lt;a class=&quot;w-headline-link&quot; href=&quot;#default-banners-on-process-completion-and-on-high-traffic-pages&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The team decided to show an installation banner when a user completes a task or is on high-traffic pages but idle (that is, not taking an action, such as scrolling or filling out a form). This approach allowed them to avoid interrupting the user&#39;s activity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/truebil-lite/default-banners.png&quot; alt=&quot;Screenshots of Truebil Lite&#39;s installation banner.&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;contextual-prompts-for-mature-users&quot;&gt;Contextual prompts for mature users &lt;a class=&quot;w-headline-link&quot; href=&quot;#contextual-prompts-for-mature-users&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For users who had interacted with the app for a while, the team used highly contextual custom messages to show the value of installing the app to the home screen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/truebil-lite/contextual-prompts.png&quot; alt=&quot;Screenshots of Truebil Lite&#39;s contextual installation prompts for mature users.&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;a-custom-banner-for-time-based-prompts&quot;&gt;A custom banner for time-based prompts &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-custom-banner-for-time-based-prompts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Finally, the team built in a non-intrusive banner with a notification-like design that&#39;s triggered at specific events, such as opening a listing page or after the user has spent a set amount of time spent in the app:&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/truebil-lite/notification.png&quot; alt=&quot;A screenshot of Truebil Lite&#39;s time-based installation prompt banner.&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Because of these improvements, Truebil&#39;s conversion and engagement rates have grown significantly with &lt;strong&gt;26% longer user sessions&lt;/strong&gt; and &lt;strong&gt;61% more conversions&lt;/strong&gt;, which is significant for their business given the high transaction value of each conversion.&lt;/p&gt;
&lt;blockquote class=&quot;w-blockquote&quot;&gt;
  &lt;p&gt;For a startup with limited resources, choosing the right platform can be critical to the success of the business. Moving to a PWA focused on speed, resilience, and engagement, enabled us to increase our revenue-to-marketing spend by &lt;strong&gt;80%&lt;/strong&gt; thanks to increased conversions and the frictionless reach of the web.&lt;/p&gt;
  &lt;p&gt;—Rakesh Raman, Co-Founder and Chief of Product &amp; Data Science at Truebil&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;w-stats&quot;&gt;
  &lt;div class=&quot;w-stat&quot;&gt;
    &lt;p class=&quot;w-stat__figure&quot;&gt;44&lt;sub class=&quot;w-stat__sub&quot;&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p class=&quot;w-stat__desc&quot;&gt;Improvement in loading time&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-stat&quot;&gt;
    &lt;p class=&quot;w-stat__figure&quot;&gt;26&lt;sub class=&quot;w-stat__sub&quot;&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p class=&quot;w-stat__desc&quot;&gt;Longer user sessions&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-stat&quot;&gt;
    &lt;p class=&quot;w-stat__figure&quot;&gt;61&lt;sub class=&quot;w-stat__sub&quot;&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p class=&quot;w-stat__desc&quot;&gt;Increase in conversions&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-stat&quot;&gt;
    &lt;p class=&quot;w-stat__figure&quot;&gt;80&lt;sub class=&quot;w-stat__sub&quot;&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p class=&quot;w-stat__desc&quot;&gt;Increase in revenue-to-marketing spend&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How to install the Thumbor image CDN</title>
    <link href="https://web.dev/install-thumbor/"/>
    <updated>2019-08-13T17:00:00-07:00</updated>
    <id>https://web.dev/install-thumbor/</id>
    <content type="html">&lt;p&gt;Image CDNs make it easy to dynamically optimize the aesthetics and performance of your images. Unlike most image CDNs, &lt;a href=&quot;http://thumbor.org/&quot;&gt;Thumbor&lt;/a&gt; is open-source and can be used for free to resize, compress, and transform images. It&#39;s suitable for production use; &lt;a href=&quot;https://wikitech.wikimedia.org/wiki/Thumbor&quot;&gt;Wikipedia&lt;/a&gt; and &lt;a href=&quot;https://medium.com/square-corner-blog/dynamic-images-with-thumbor-a430a1cfcd87&quot;&gt;Square&lt;/a&gt; both use Thumbor.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Thumbor on your own server. Once installed, you&#39;ll be able to use Thumbor as an API for transforming your images.&lt;/p&gt;
&lt;h2 id=&quot;intro&quot;&gt;Intro &lt;a class=&quot;w-headline-link&quot; href=&quot;#intro&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll be installing Thumbor on a VM running Ubuntu 16.04. Ubuntu 16.04 is a very common image and these instructions are intended to work on any cloud provider. Creating a VM might sound like more work than installing Thumbor on your local machine, but the minutes that you take to create a VM will probably save you hours or days of frustration trying to get Thumbor to properly install on your local machine. Although easy to use, Thumbor is notoriously difficult to install but these instructions simplify the process. If dependencies download quickly, the installation can be completed in 5 to 10 minutes.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites &lt;a class=&quot;w-headline-link&quot; href=&quot;#prerequisites&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This post assumes that you know how to create a Ubuntu 16.04 LTS VM on a cloud platform like &lt;a href=&quot;https://cloud.google.com/compute/docs/instances/create-start-instance&quot;&gt;Google Cloud&lt;/a&gt;, &lt;a href=&quot;https://aws.amazon.com/getting-started/tutorials/launch-a-virtual-machine/&quot;&gt;AWS,&lt;/a&gt; or &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/linux/quick-create-portal?toc=%2Fazure%2Fvirtual-machines%2Flinux%2Ftoc.json&quot;&gt;Azure&lt;/a&gt; and how to use command line tools to set up the VM.&lt;/p&gt;
&lt;h2 id=&quot;install-thumbor-dependencies&quot;&gt;Install Thumbor Dependencies &lt;a class=&quot;w-headline-link&quot; href=&quot;#install-thumbor-dependencies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Update and upgrade Ubuntu&#39;s already-installed packages:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update -y &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; upgrade -y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install &lt;code&gt;pip&lt;/code&gt;, the package manager for Python. Later you&#39;ll install Thumbor with &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y python-pip&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install Thumbor&#39;s dependencies. Thumbor&#39;s documentation does not explicitly mention these dependencies, but Thumbor will not install successfully without them.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ssl packages&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y libcurl4-openssl-dev libssl-dev&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# computer vision packages&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y python-opencv libopencv-dev&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# image format packages&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y libjpeg-dev libpng-dev libwebp-dev webp&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;install-thumbor&quot;&gt;Install Thumbor &lt;a class=&quot;w-headline-link&quot; href=&quot;#install-thumbor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install Thumbor using pip.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; thumbor&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: Many Python developers use &lt;a href=&quot;https://pypi.org/project/virtualenv/&quot;&gt;virtualenv&lt;/a&gt; to manage their packages. For the sake of simplicity, these instructions do not use &lt;code&gt;virtualenv&lt;/code&gt;. If you are installing Thumbor in a standalone environment, &lt;code&gt;virtualenv&lt;/code&gt; is not necessary. If you choose to use &lt;code&gt;virtualenv&lt;/code&gt;, note that Thumbor requires Python 2.7 and will not work with newer versions of &lt;code&gt;pip&lt;/code&gt; (e.g. these instructions use &lt;code&gt;pip&lt;/code&gt; 8.1.1).&lt;/p&gt;
&lt;p&gt;If you&#39;ve successfully installed Thumbor, this should work:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;thumbor --help&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;run-thumbor&quot;&gt;Run Thumbor &lt;a class=&quot;w-headline-link&quot; href=&quot;#run-thumbor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Thumbor. Debug logging is optional but can be helpful when you&#39;re getting started.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;thumbor --log-level debug&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thumbor is now running.&lt;/p&gt;
&lt;h2 id=&quot;open-firewall-port&quot;&gt;Open Firewall Port &lt;a class=&quot;w-headline-link&quot; href=&quot;#open-firewall-port&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, Thumbor runs on port 8888. If your VM&#39;s IP address is &lt;code&gt;12.123.12.122&lt;/code&gt;, then you would access Thumbor from the web browser at &lt;code&gt;http://12.123.12.123:8888/.../$IMAGE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, this probably won&#39;t work for you (yet) because cloud providers usually require that you explicitly open firewall ports before they will accept incoming traffic.&lt;/p&gt;
&lt;p&gt;Update the firewall to expose port 8888. Here&#39;s more information on how to do this for: &lt;a href=&quot;https://cloud.google.com/vpc/docs/using-firewalls&quot;&gt;Google Cloud&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html&quot;&gt;AWS&lt;/a&gt;, and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/windows/nsg-quickstart-portal&quot;&gt;Azure&lt;/a&gt;. Note that for Google Cloud you need to first &lt;a href=&quot;https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address&quot;&gt;assign a static IP address to your VM&lt;/a&gt; and then &lt;a href=&quot;https://cloud.google.com/vpc/docs/special-configurations#externalhttpconnection&quot;&gt;allow an external HTTP connection&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;try-it-out&quot;&gt;Try It Out &lt;a class=&quot;w-headline-link&quot; href=&quot;#try-it-out&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thumbor is now accessible and ready for use. Try it out by visiting the following URL:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;http://YOUR_VIRTUAL_MACHINE:8888/unsafe/100x100/https://web.dev/install-thumbor/hero.jpg&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this URL uses HTTP. Thumbor uses HTTP by default but can be &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/image_loader.html&quot;&gt;configured&lt;/a&gt; to use HTTPS.&lt;/p&gt;
&lt;p&gt;You should see an image that is 100 pixels wide by 100 pixels tall. Thumbor has taken the image &lt;code&gt;hero.jpg&lt;/code&gt; and size specified in the URL string and served the result. You can replace the image in the URL string (i.e &lt;code&gt;https://web.dev/install-thumbor/hero.jpg&lt;/code&gt;) with any other image (e.g. &lt;code&gt;https://your-site.com/cat.jpg&lt;/code&gt;) and Thumbor will resize that image too.&lt;/p&gt;
&lt;h2 id=&quot;appendix:-configuring-systemd&quot;&gt;Appendix: Configuring Systemd &lt;a class=&quot;w-headline-link&quot; href=&quot;#appendix:-configuring-systemd&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This step explains how to make sure that the Thumbor process keeps running, even after the VM has been restarted. This step is important for production sites, but optional if you&#39;re just playing around with Thumbor.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/systemd.html&quot;&gt;Systemd&lt;/a&gt; is the &amp;quot;system and service manager&amp;quot; for Linux operating systems. &lt;code&gt;systemd&lt;/code&gt; makes it easy to configure when services (processes) run.&lt;/p&gt;
&lt;p&gt;You will be configuring &lt;code&gt;systemd&lt;/code&gt; to automatically start Thumbor on VM boot. If the VM is restarted, the Thumbor process will automatically restart as well. This is much more reliable than relying on user intervention to start Thumbor.&lt;/p&gt;
&lt;p&gt;Navigate to the &lt;code&gt;/lib/systemd/system&lt;/code&gt; directory. This directory contains the service files for &lt;code&gt;systemd&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /lib/systemd/system&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As superuser, create a &lt;code&gt;thumbor.service&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; thumbor.service&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using your favorite text editor (vim and nano come pre-installed on Ubuntu or you can install another editor), add the following configuration to &lt;code&gt;thumbor.service&lt;/code&gt;. This configuration will run &lt;code&gt;/usr/local/bin/thumbor&lt;/code&gt; (i.e. the Thumbor binary) once networking is available and will restart Thumbor on &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=&quot;&gt;failure&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;[Unit]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;Description=Service for Thumbor image CDN&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;Documentation=https://thumbor.readthedocs.io/en/latest/&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;After=network.target&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;[Service]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;ExecStart=/usr/local/bin/thumbor&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;Restart=on-failure&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;[Install]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;WantedBy=multi-user.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;systemctl&lt;/code&gt; is the utility used to manage &lt;code&gt;systemd&lt;/code&gt;. Use the &lt;code&gt;start&lt;/code&gt; command to start Thumbor.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start thumbor.service&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: If Thumbor is currently running, you should stop it before attempting to start Thumbor using &lt;code&gt;systemctl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, &amp;quot;enable&amp;quot; Thumbor. This means that Thumbor will automatically start on boot.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; thumbor.service&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that you&#39;ve successfully configured &lt;code&gt;systemd&lt;/code&gt; by running the &lt;code&gt;status&lt;/code&gt; command.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;systemctl status thumbor.service&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;ve successfully set up thumbor.service to use &lt;code&gt;systemd&lt;/code&gt;, the &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/systemctl.html#status%20PATTERN%E2%80%A6%7CPID%E2%80%A6%5D&quot;&gt;status&lt;/a&gt; should show that it is enabled and active.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/install-thumbor/systemd.jpg&quot; alt=&quot;Systemctl displaying the status of Thumbor&quot; class=&quot;w-screenshot&quot;&gt;
&lt;/figure&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Third-party JavaScript performance</title>
    <link href="https://web.dev/third-party-javascript/"/>
    <updated>2019-08-12T17:00:00-07:00</updated>
    <id>https://web.dev/third-party-javascript/</id>
    <content type="html">&lt;p&gt;Third-party JavaScript generally refers to scripts embedded in your website that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not authored by you&lt;/li&gt;
&lt;li&gt;Served from third-party servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sites use these scripts for various purposes, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Social sharing buttons&lt;/li&gt;
&lt;li&gt;Video player embeds&lt;/li&gt;
&lt;li&gt;Chat services&lt;/li&gt;
&lt;li&gt;Advertising iframes&lt;/li&gt;
&lt;li&gt;Analytics and metrics scripts&lt;/li&gt;
&lt;li&gt;A/B testing scripts for experiments&lt;/li&gt;
&lt;li&gt;Helper libraries (like date formatting, animation, and functional libraries)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;
    &lt;source src=&quot;https://web.dev/third-party-javascript/third-party-examples.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;Third-party scripts can provide powerful functionality, but that&#39;s not the whole story. They also affect privacy, security, and page behavior⁠—and they can be particularly problematic for performance.&lt;/p&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;w-headline-link&quot; href=&quot;#performance&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Any significant amount of &lt;a href=&quot;https://web.dev/bootup-time/#how-javascript-execution-slows-down-performance&quot;&gt;JavaScript can slow down performance&lt;/a&gt;. But because third-party JavaScript is usually outside your control, it can bring additional issues.&lt;/p&gt;
&lt;h3 id=&quot;network&quot;&gt;Network &lt;a class=&quot;w-headline-link&quot; href=&quot;#network&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Setting up connections takes time, and sending too many requests to multiple servers causes slowdowns. That time is even longer for secure connections, which may involve DNS lookups, redirects, and several round trips to the final server that handles the user&#39;s request.&lt;/p&gt;
&lt;p&gt;Third-party scripts often add to network overhead with things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firing additional network requests&lt;/li&gt;
&lt;li&gt;Pulling in unoptimized images and videos&lt;/li&gt;
&lt;li&gt;Insufficient &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching&quot;&gt;HTTP caching&lt;/a&gt;, which forces frequent fetching of network resources&lt;/li&gt;
&lt;li&gt;Insufficient &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer&quot;&gt;server compression&lt;/a&gt; of resources&lt;/li&gt;
&lt;li&gt;Multiple instances of frameworks and libraries pulled in by different third-party embeds&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rendering&quot;&gt;Rendering &lt;a class=&quot;w-headline-link&quot; href=&quot;#rendering&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The way third-party JavaScript is loaded matters a lot. If it&#39;s done synchronously in the critical rendering path it delays parsing of the rest of the document.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--key-term&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key Term:&lt;/strong&gt;
The &lt;strong&gt;critical rendering path&lt;/strong&gt; includes all resources that the browser needs to display the first screen&#39;s worth of content.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If a third party has server issues and fails to deliver a resource, rendering is blocked until the request times out, which can be anywhere from 10 to 80 seconds. You can test and simulate this problem with &lt;a href=&quot;https://css-tricks.com/use-webpagetest-api/#single-point-of-failure&quot;&gt;WebPageTest Single-Point-of-Failure tests&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/#ab_test_smaller_samples_of_users&quot;&gt;A/B testing scripts&lt;/a&gt; can also often delay rendering. Most of them block content display until they complete processing—which can be true even for asynchronous A/B testing scripts.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;what-to-do-about-it&quot;&gt;What to do about it &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-to-do-about-it&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using third-party JavaScript is often unavoidable, but there are things you can do to minimize adverse effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When choosing third-party resources, favor those that send the least amount of code while still giving you the functionality you need.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/&quot;&gt;performance budgets&lt;/a&gt; for third-party content to keep their cost in check.&lt;/li&gt;
&lt;li&gt;Don&#39;t use the same functionality from two different vendors. You probably don&#39;t need two tag managers or two analytics platforms.&lt;/li&gt;
&lt;li&gt;Routinely audit and clean out redundant third-party scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn how to audit third-party content and load it efficiently for better performance and user experience, check out the other posts in the &lt;a href=&quot;https://web.dev/fast/#optimize-your-third-party-resources&quot;&gt;Optimize your third-party resources&lt;/a&gt; section.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>More capable form controls</title>
    <link href="https://web.dev/more-capable-form-controls/"/>
    <updated>2019-08-07T17:00:00-07:00</updated>
    <id>https://web.dev/more-capable-form-controls/</id>
    <content type="html">&lt;p&gt;Many developers build custom form controls, either to provide controls that aren&#39;t built in to the browser, or to customize the look and feel beyond what&#39;s possible with the native form controls.&lt;/p&gt;
&lt;p&gt;However, it can be difficult to replicate the features of built-in HTML form controls. Consider some of the features an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element gets automatically when you add it to a form:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The input is automatically added to the form&#39;s list of controls.&lt;/li&gt;
&lt;li&gt;The input&#39;s value is automatically submitted with the form.&lt;/li&gt;
&lt;li&gt;The input participates in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation&quot;&gt;form validation&lt;/a&gt;. You can style the input using the &lt;code&gt;:valid&lt;/code&gt; and &lt;code&gt;:invalid&lt;/code&gt; pseudoclasses.&lt;/li&gt;
&lt;li&gt;The input is notified when the form is reset, when the form is reloaded, or when the browser tries to autofill form entries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Custom form controls typically have few of these features. Developers can work around some of the limitations in JavaScript, like adding a hidden &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; to a form to participate in form submission. But other features just can&#39;t be replicated in JavaScript alone.&lt;/p&gt;
&lt;p&gt;Two new web features make it easier to build custom form controls, and remove the limitations of current custom controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;formdata&lt;/code&gt; event lets an arbitrary JavaScript object participate in form submission, so you can add form data without using a hidden &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The Form-associated custom elements API lets custom elements act more like built-in form controls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two features can be used to create new kinds of controls that work better with native web forms.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Building custom form controls is an advanced topic. This article assumes a certain knowledge of forms and form controls. When building a custom form control, there are many factors to consider, especially making sure that your controls are accessible to all users. To learn more about forms, go to the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms&quot;&gt;MDN guide on forms&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;event-based-api&quot;&gt;Event-based API &lt;a class=&quot;w-headline-link&quot; href=&quot;#event-based-api&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;formdata&lt;/code&gt; event is a low-level API that lets any JavaScript code participate in form submission. The mechanism works like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You add a &lt;code&gt;formdata&lt;/code&gt; event listener to the form you want to interact with.&lt;/li&gt;
&lt;li&gt;When a user clicks the submit button, the form fires a &lt;code&gt;formdata&lt;/code&gt; event, which includes a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FormData&quot;&gt;&lt;code&gt;FormData&lt;/code&gt;&lt;/a&gt; object that holds all of the data being submitted.&lt;/li&gt;
&lt;li&gt;Each &lt;code&gt;formdata&lt;/code&gt; listener gets a chance to add to or modify the data before the form is submitted.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&#39;s an example of sending a single value in a &lt;code&gt;formdata&lt;/code&gt; event listener:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;form&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// FormData event is sent on &amp;lt;form&gt; submission, before transmission.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// The event has a formData property&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;formdata&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;formData&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// https://developer.mozilla.org/en-US/docs/Web/API/FormData&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  formData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;my-input&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; myInputValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Try this out using our example on Glitch. Be sure to run it on Chrome 77 or later to see the API in action.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; encrypted-media&quot; src=&quot;https://glitch.com/embed/#!/embed/formdata-event?path=index.html&amp;amp;previewSize=0&quot; alt=&quot;formdata event demo on Glitch&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;form-associated-custom-elements&quot;&gt;Form-associated custom elements &lt;a class=&quot;w-headline-link&quot; href=&quot;#form-associated-custom-elements&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use the event-based API with any kind of component, but it only allows you to interact with the submission process.&lt;/p&gt;
&lt;p&gt;Native form controls participate in many parts of the form lifecycle besides submission. Form-associated custom elements aim to bridge the gap between custom widgets and native controls. Form-associated custom elements match many of the features of native form elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you place a form-associated custom element inside a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, it&#39;s automatically associated with the form, like a native control.&lt;/li&gt;
&lt;li&gt;The element can be labeled using a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;The element can set a value that&#39;s automatically submitted with the form.&lt;/li&gt;
&lt;li&gt;The element can set a flag indicating whether or not it has valid input. If one of the form controls has invalid input, the form can&#39;t be submitted.&lt;/li&gt;
&lt;li&gt;The element can provide callbacks for various parts of the form lifecycle—such as when the form is disabled or reset to its default state.&lt;/li&gt;
&lt;li&gt;The element supports the standard CSS pseudoclasses for form controls, like &lt;code&gt;:disabled&lt;/code&gt; and &lt;code&gt;:invalid&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s a lot of features! This article won&#39;t touch on all of them, but will describe the basics needed to integrate your custom element with a form.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;This section assumes a basic familiarity with custom elements. For an introduction to custom elements, see &lt;a href=&quot;https://developers.google.com/web/fundamentals/web-components/customelements&quot;&gt;Custom Elements v1: Reusable Web Components&lt;/a&gt; on Web Fundamentals.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;defining-a-form-associated-custom-element&quot;&gt;Defining a form-associated custom element &lt;a class=&quot;w-headline-link&quot; href=&quot;#defining-a-form-associated-custom-element&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To turn a custom element into a form-associated custom element requires a few extra steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a static &lt;code&gt;formAssociated&lt;/code&gt; property to your custom element class. This tells the browser to treat the element like a form control.&lt;/li&gt;
&lt;li&gt;Call the &lt;code&gt;attachInternals()&lt;/code&gt; method on the element to get access to extra methods and properties for form controls, like &lt;code&gt;setFormValue()&lt;/code&gt; and &lt;code&gt;setValidity()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the common properties and methods supported by form controls, like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, and &lt;code&gt;validity&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s how those items fit into a basic custom element definition:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Form-associated custom elements must be autonomous custom elements--&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// meaning they must extend HTMLElement, not one of its subclasses.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyCounter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Identify the element as a form-associated custom element&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; formAssociated &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Get access to the internal form control APIs&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attachInternals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// internal value for this control&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Form controls usually expose a &quot;value&quot; property&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// The following properties and methods aren&#39;t strictly required, &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// but native form controls provide them. Providing them helps&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// ensure consistency with native controls.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;validity&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validationMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;validationMessage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;willValidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;willValidate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;checkValidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkValidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;reportValidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reportValidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  … &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;my-counter&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MyCounter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once registered, you can use this element wherever you&#39;d use a native form control:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Number of bunnies: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setting-a-value&quot;&gt;Setting a value &lt;a class=&quot;w-headline-link&quot; href=&quot;#setting-a-value&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;attachInternals()&lt;/code&gt; method returns an &lt;code&gt;ElementInternals&lt;/code&gt; object that provides access to form control APIs. The most basic of these is the &lt;code&gt;setFormValue()&lt;/code&gt; method, which sets the current value of the control.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;setFormValue()&lt;/code&gt; method can take one of three types of values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A string value.&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/File&quot;&gt;&lt;code&gt;File&lt;/code&gt;&lt;/a&gt; object.&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/FormData&quot;&gt;&lt;code&gt;FormData&lt;/code&gt;&lt;/a&gt; object. You can use a &lt;code&gt;FormData&lt;/code&gt; object to pass multiple values (for example, a credit card input control might pass a card number, expiration date, and verification code).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To set a simple value:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFormValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To set multiple values, you can do something like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Use the control&#39;s name as the base name for submitted data&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FormData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-first-name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName_&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-last-name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName_&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFormValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;The &lt;code&gt;setFormValue()&lt;/code&gt; method takes a second, optional &lt;code&gt;state&lt;/code&gt; parameter, used to store the internal state of the control. For more information, see &lt;a href=&quot;#restoring-form-state&quot;&gt;Restoring form state&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;input-validation&quot;&gt;Input validation &lt;a class=&quot;w-headline-link&quot; href=&quot;#input-validation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Your control can also participate in form validation by calling the &lt;code&gt;setValidity()&lt;/code&gt;
method on the internals object.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Assume this is called whenever the internal value is updated&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;onUpdateValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;:disabled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;required&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;customError&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Value cannot be negative.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setValidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFormValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can style a form-associated custom element with the &lt;code&gt;:valid&lt;/code&gt; and &lt;code&gt;:invalid&lt;/code&gt;
pseudoclasses, just like a built-in form control.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Although you can set a validation message, Chrome currently
fails to display the validation message for form-associated custom elements.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;lifecycle-callbacks&quot;&gt;Lifecycle callbacks &lt;a class=&quot;w-headline-link&quot; href=&quot;#lifecycle-callbacks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A form-associated custom element API includes a set of extra lifecycle callbacks to tie in to the form lifecycle. The callbacks are optional: only implement a callback if your element needs to do something at that point in the lifecycle.&lt;/p&gt;
&lt;h4 id=&quot;void-formassociatedcallback(form)&quot;&gt;&lt;code&gt;void formAssociatedCallback(form)&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#void-formassociatedcallback(form)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Called when the browser associates the element with a form element, or disassociates the element from a form element.&lt;/p&gt;
&lt;h4 id=&quot;void-formdisabledcallback(disabled)&quot;&gt;&lt;code&gt;void formDisabledCallback(disabled)&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#void-formdisabledcallback(disabled)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Called after the &lt;code&gt;disabled&lt;/code&gt; state of the element changes, either because the &lt;code&gt;disabled&lt;/code&gt; attribute of this element was added or removed; or because the &lt;code&gt;disabled&lt;/code&gt; state changed on a &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; that&#39;s an ancestor of this element. The &lt;code&gt;disabled&lt;/code&gt; parameter represents the new &lt;a href=&quot;https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled&quot;&gt;disabled state&lt;/a&gt; of the element. The element may, for example, disable elements in its shadow DOM when it is disabled.&lt;/p&gt;
&lt;h4 id=&quot;void-formresetcallback()&quot;&gt;&lt;code&gt;void formResetCallback()&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#void-formresetcallback()&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Called after the form is reset. The element should reset itself to some kind of default state. For native inputs, this usually involves setting the &lt;code&gt;value&lt;/code&gt; property to match the &lt;code&gt;value&lt;/code&gt; attribute set in markup (or in the case of a checkbox, setting the &lt;code&gt;checked&lt;/code&gt; property to match the &lt;code&gt;checked&lt;/code&gt; attribute.&lt;/p&gt;
&lt;h4 id=&quot;void-formstaterestorecallback(state-mode)&quot;&gt;&lt;code&gt;void formStateRestoreCallback(state, mode)&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#void-formstaterestorecallback(state-mode)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Called in one of two circumstances:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the browser restores the state of the element (for example, after a navigation, or when the browser restarts).  The &lt;code&gt;mode&lt;/code&gt; argument is &lt;code&gt;&amp;quot;restore&amp;quot;&lt;/code&gt; in this case.&lt;/li&gt;
&lt;li&gt;When the browser&#39;s input-assist features such as form autofilling sets a value. The &lt;code&gt;mode&lt;/code&gt; argument is &lt;code&gt;&amp;quot;autocomplete&amp;quot;&lt;/code&gt; in this case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The type of the first argument depends on how the &lt;code&gt;setFormValue()&lt;/code&gt; method was called. For more details, see &lt;a href=&quot;#restoring-form-state&quot;&gt;Restoring form state&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;restoring-form-state&quot;&gt;Restoring form state &lt;a class=&quot;w-headline-link&quot; href=&quot;#restoring-form-state&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Under some circumstances—like when navigating back to a page, or restarting the browser, the browser may try to restore the form to the state the user left it in.&lt;/p&gt;
&lt;p&gt;For a form-associated custom element, the restored state comes from the value(s) you pass to the &lt;code&gt;setFormValue()&lt;/code&gt; method. You can call the method with a single value parameter, as shown in the &lt;a href=&quot;#set-a-value&quot;&gt;earlier examples&lt;/a&gt;, or with two parameters:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFormValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;value&lt;/code&gt; represents the submittable value of the control. The optional &lt;code&gt;state&lt;/code&gt; parameter is an &lt;em&gt;internal&lt;/em&gt; representation of the state of the control, which can include data that doesn&#39;t get sent to the server. The &lt;code&gt;state&lt;/code&gt; parameter takes the same types as the &lt;code&gt;value&lt;/code&gt; parameter—it can be a string, &lt;code&gt;File&lt;/code&gt;, or &lt;code&gt;FormData&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;state&lt;/code&gt; parameter is useful when you can&#39;t restore a control&#39;s state based on the value alone. For example, suppose you create a color picker with multiple modes: a palette or an RGB color wheel. The submittable &lt;em&gt;value&lt;/em&gt; would be the selected color in a canonical form, like &lt;code&gt;&amp;quot;#7fff00&amp;quot;&lt;/code&gt;. But to restore the control to a specific state, you&#39;d also need to know which mode it was in, so the &lt;em&gt;state&lt;/em&gt; might look like &lt;code&gt;&amp;quot;palette/#7fff00&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_internals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setFormValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_mode &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your code would need to restore its state based on the stored state value.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;formStateRestoreCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mode &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;restore&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// expects a state parameter in the form &#39;controlMode/value&#39;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;controlMode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_mode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; controlMode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Chrome currently doesn&#39;t handle autofill for form-associated &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// custom elements. In the autofill case, you might need to handle&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// a raw value.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the case of a simpler control (for example a number input), the value is probably sufficient to restore the control to its previous state. If you omit &lt;code&gt;state&lt;/code&gt; when calling &lt;code&gt;setFormValue()&lt;/code&gt;, then the value is passed to &lt;code&gt;formStateRestoreCallback()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;formStateRestoreCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Simple case, restore the saved value&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;a-working-example&quot;&gt;A working example &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-working-example&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following example puts together many of the features of form-associated custom elements.
Be sure to run it on Chrome 77 or later to see the API in action.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; encrypted-media&quot; src=&quot;https://glitch.com/embed/#!/embed/form-associated-ce?path=public%2Fmy-control.js&amp;amp;previewSize=0&quot; alt=&quot;formdata event demo on Glitch&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;feature-detection&quot;&gt;Feature detection &lt;a class=&quot;w-headline-link&quot; href=&quot;#feature-detection&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use feature detection to determine whether the &lt;code&gt;formdata&lt;/code&gt; event and form-associated custom elements are available. There are currently no polyfills released for either feature. In both cases, you can fall back to adding a hidden form element to propagate the control&#39;s value to the form. Many of the more advanced features of form-associated custom elements will likely be difficult or impossible to polyfill.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;FormDataEvent&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// formdata event is supported&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ElementInternals&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;setFormData&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ElementInternals&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Form-associated custom elements are supported&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;formdata&lt;/code&gt; event and form-associated custom elements provide new tools for creating custom form controls.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;formdata&lt;/code&gt; event doesn&#39;t give you any new capabilities, but it gives you an interface for adding your form data to the submit process, without having to create a hidden &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;The form-associated custom elements API provides a new set of capabilities for making custom form controls that work like built-in form controls.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image by Oudom Pravat on &lt;a href=&quot;https://unsplash.com/photos/yQi4mAFmRew&quot;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Largest Contentful Paint</title>
    <link href="https://web.dev/largest-contentful-paint/"/>
    <updated>2019-08-07T17:00:00-07:00</updated>
    <id>https://web.dev/largest-contentful-paint/</id>
    <content type="html">&lt;p&gt;Historically, it&#39;s been a challenge for web developers to measure how quickly
the main content of a web page loads and is visible to users.&lt;/p&gt;
&lt;p&gt;Older metrics like
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Events/load&quot;&gt;load&lt;/a&gt; or
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded&quot;&gt;DOMContentLoaded&lt;/a&gt;
are not good because they don&#39;t necessarily correspond to what the user sees on
their screen. And newer, user-centric performance metrics like &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint&quot;&gt;First Paint
(FP) and First Contentful Paint
(FCP)&lt;/a&gt;
only capture the very beginning of the loading experience. If a page shows a
splash screen or displays a loading indicator, this moment is not very relevant
to the user.&lt;/p&gt;
&lt;p&gt;In the past we&#39;ve recommended performance metrics like &lt;a href=&quot;https://web.dev/first-meaningful-paint/&quot;&gt;First Meaningful Paint
(FMP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/speed-index/&quot;&gt;Speed Index
(SI)&lt;/a&gt; (both available in Lighthouse) to help
capture more of the loading experience after the initial paint, but these
metrics are complex, hard to explain, and often wrong—meaning they still do
not identify when the main content of the page has loaded.&lt;/p&gt;
&lt;p&gt;Sometimes simpler is better. Based on discussions in the &lt;a href=&quot;https://www.w3.org/webperf/&quot;&gt;W3C Web
Performance Working Group&lt;/a&gt; and research done at
Google, we&#39;ve found that a more accurate way to measure when the main content
of a page is loaded is to look at when the largest element was rendered.&lt;/p&gt;
&lt;h2 id=&quot;largest-contentful-paint-defined&quot;&gt;Largest Contentful Paint defined &lt;a class=&quot;w-headline-link&quot; href=&quot;#largest-contentful-paint-defined&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://wicg.github.io/largest-contentful-paint/&quot;&gt;Largest Contentful Paint
(LCP)&lt;/a&gt; API, available in
Chrome 77, reports the render time of the largest content element visible
in the viewport.&lt;/p&gt;
&lt;h3 id=&quot;what-elements-are-considered&quot;&gt;What elements are considered? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-elements-are-considered&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As currently specified, the types of elements considered for Largest Contentful
Paint are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; elements inside an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements (the poster image is used)&lt;/li&gt;
&lt;li&gt;An element with a background image loaded via the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/url()&quot;&gt;&lt;code&gt;url()&lt;/code&gt;&lt;/a&gt; function
(as opposed to a
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Images/Using_CSS_gradients&quot;&gt;CSS gradient&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements&quot;&gt;Block-level&lt;/a&gt;
elements containing text nodes or other inline-level text elements children.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note, restricting the elements to this limited set was intentional in order to
keep things simple in the beginning. Additional elements (e.g. &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;,
&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;) may be added in the future as more research is conducted.&lt;/p&gt;
&lt;h3 id=&quot;how-is-an-element&#39;s-size-determined&quot;&gt;How is an element&#39;s size determined? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-is-an-element&#39;s-size-determined&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The size of the element reported for Largest Contentful Paint is typically the
size that&#39;s visible to the user within the viewport. If the element extends
outside of the viewport, or if any of the element is clipped or has non-visible
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/overflow&quot;&gt;overflow&lt;/a&gt;, those
portions do not count toward the element&#39;s size.&lt;/p&gt;
&lt;p&gt;For image elements that have been resized from their &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Intrinsic_Size&quot;&gt;intrinsic
size&lt;/a&gt;, the
size that gets reported is either the visible size or the intrinsic size,
whichever is smaller. For example, images that are shrunk down to a much
smaller than their intrinsic size will only report the size they&#39;re displayed
at, whereas images that are stretched or expanded to a larger size will only
report their intrinsic sizes.&lt;/p&gt;
&lt;p&gt;For text elements, only the size of their text nodes is considered (the smallest
rectangle that encompases all text nodes).&lt;/p&gt;
&lt;p&gt;For all elements, any margin, padding, or border applied via CSS is not considered.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Determining which text nodes belong to which elements can sometimes
be tricky, especially for elements whose children includes inline elements and
plain text nodes but also block-level elements. The key point is that every
text node belongs to (and only to) its closest block-level ancestor element.
In &lt;a href=&quot;https://wicg.github.io/element-timing/#set-of-owned-text-nodes&quot;&gt;spec
terms&lt;/a&gt;:
each text node belongs to the element that generates its &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block&quot;&gt;containing
block&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;when-is-largest-contentful-paint-reported&quot;&gt;When is largest contentful paint reported? &lt;a class=&quot;w-headline-link&quot; href=&quot;#when-is-largest-contentful-paint-reported&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web pages often load in stages, and as a result, it&#39;s possible that the largest
element on the page might change.&lt;/p&gt;
&lt;p&gt;To handle this potential for change, the browser dispatches a
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry&quot;&gt;&lt;code&gt;PerformanceEntry&lt;/code&gt;&lt;/a&gt;
of type &lt;code&gt;largest-contentful-paint&lt;/code&gt; identifying the largest contentful element
as soon as the browser has painted the first frame. But then, after rendering
subsequent frames, it will dispatch another
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry&quot;&gt;&lt;code&gt;PerformanceEntry&lt;/code&gt;&lt;/a&gt;
any time the largest contentful element changes.&lt;/p&gt;
&lt;p&gt;It&#39;s important to note that an element can only be considered the largest
contentful element once it has rendered and is visible to the user. Images that
have not yet loaded are not considered &amp;quot;rendered&amp;quot;. Neither are text nodes using
web fonts during the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display#The_font_display_timeline&quot;&gt;font block
period&lt;/a&gt;.
In such cases a smaller element may be reported as the largest contentful
element, but as soon as the larger element finishes rendering, it&#39;ll be
reported via another  &lt;code&gt;PerformanceEntry&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;In addition to late-loading images and fonts, a page may add new elements to
the DOM as new content becomes available. If any of these new elements is
larger than the previous largest contentful element, a new &lt;code&gt;PerformanceEntry&lt;/code&gt;
will also be reported.&lt;/p&gt;
&lt;p&gt;If a page removes an element from the DOM, that element will no longer be
considered. Similarly, if an element&#39;s associated image resource changes (e.g.
changing &lt;code&gt;img.src&lt;/code&gt; via JavaScript), then that element will stop be considered
until the new image loads.&lt;/p&gt;
&lt;p&gt;The browser will stop reporting new entries as soon as the user interacts with
the page (via a tap, scroll, or keypress), as user interaction often changes
what&#39;s visible to the user (which is especially true with scrolling).&lt;/p&gt;
&lt;p&gt;For analysis purposes, you should only report the most recently dispatched
&lt;code&gt;PerformanceEntry&lt;/code&gt; to your analytics service.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
Since users can open pages in a background tab, it&#39;s possible that the largest
contentful paint will not happen until the user focuses the tab, which can be
much later than when they first loaded it.&lt;/p&gt;
&lt;/div&gt;
&lt;h4 id=&quot;load-time-vs.-render-time&quot;&gt;Load time vs. render time &lt;a class=&quot;w-headline-link&quot; href=&quot;#load-time-vs.-render-time&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For security reasons, the render timestamp of images is not exposed for
cross-origin images that lack the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin&quot;&gt;&lt;code&gt;Timing-Allow-Origin&lt;/code&gt;&lt;/a&gt;
header. Instead, only their load time is exposed (since this is already exposed
via many other web APIs).&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;#how-to-measure-largest-contentful-paint-in-javascript&quot;&gt;usage example&lt;/a&gt;
below shows how to handle elements whose render time is not available. But,
when possible, it&#39;s always recommended to set the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin&quot;&gt;&lt;code&gt;Timing-Allow-Origin&lt;/code&gt;&lt;/a&gt;
header, so your metrics will be more accurate.&lt;/p&gt;
&lt;h3 id=&quot;how-are-element-layout-and-size-changes-handled&quot;&gt;How are element layout and size changes handled? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-are-element-layout-and-size-changes-handled&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To keep the performance overhead of calculating and dispatching new performance
entries low, changes to an element&#39;s size or position do not generate new LCP
candidates. Only the element&#39;s initial size and position in the viewport is
considered.&lt;/p&gt;
&lt;p&gt;This means images that are initially rendered off-screen and then transition
on-screen may not be reported. It also means elements initially rendered in the
viewport that then get pushed down, out of view will still report their
initial, in-viewport size.&lt;/p&gt;
&lt;p&gt;However, (as mentioned above) an element will be removed from consideration if
it&#39;s removed from the DOM or if its associated image resource changes.&lt;/p&gt;
&lt;h3 id=&quot;examples&quot;&gt;Examples &lt;a class=&quot;w-headline-link&quot; href=&quot;#examples&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here are some examples of when the Largest Contentful Paint occurs on a few
popular websites:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/largest-contentful-paint/lcp-cnn-filmstrip.png&quot; alt=&quot;Largest Contentful Paint timeline from cnn.com&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/largest-contentful-paint/lcp-techcrunch-filmstrip.png&quot; alt=&quot;Largest Contentful Paint timeline from techcrunch.com&quot;&gt;&lt;/p&gt;
&lt;p&gt;In both of the timelines above, the largest element changes as content loads.
In the first example, new content is added to the DOM and that changes what
element is the largest. In the second example, the layout changes and content
that was previously the largest is removed from the viewport.&lt;/p&gt;
&lt;p&gt;While it&#39;s often the case that late-loading content is larger than content
already on the page, that&#39;s not necessarily the case. The next two examples
show the Largest Contentful Paint occurring before the page fully loads.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/largest-contentful-paint/lcp-instagram-filmstrip.png&quot; alt=&quot;Largest Contentful Paint timeline from instagram.com&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/largest-contentful-paint/lcp-google-filmstrip.png&quot; alt=&quot;Largest Contentful Paint timeline from google.com&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the first example, the Instagram logo is loaded relatively early and it
remains the largest element even as other content is progressively shown. In
the Google search results page example, the largest element is a paragraph of
text that is displayed before any of the images or logo finish loading. Since
all the individual images are smaller than this paragraph, it remains the
largest element throughout the load process.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;In the first frame of the Instagram timeline, you may notice the camera logo
does not have a green box around it. That&#39;s because it&#39;s an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element,
and &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; elements are not currently considered LCP candidates. The first
LCP candidate is the text in the second frame.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;how-to-measure-largest-contentful-paint-in-javascript&quot;&gt;How to measure Largest Contentful Paint in JavaScript &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-measure-largest-contentful-paint-in-javascript&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following code block shows how to measure LCP in JavaScript and report it
to an analytics service when the user leaves the tab:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create a variable to hold the latest LCP value (since it can change).&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lcp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create the PerformanceObserver instance.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; po &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entryList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entryList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lastEntry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entries&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Update `lcp` to the latest value, using `renderTime` if it&#39;s available,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// otherwise using `loadTime`. (Note: `renderTime` may not be available if&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// the element is an image and it&#39;s loaded cross-origin without the&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// `Timing-Allow-Origin` header.)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  lcp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lastEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderTime &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; lastEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loadTime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Observe entries of type `largest-contentful-paint`, including buffered&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// entries, i.e. entries that occurred before calling `observe()`.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;po&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;largest-contentful-paint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffered&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Send the latest LCP value to your analytics server once the user&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// leaves the tab.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visibilitychange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lcp &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;visibilityState &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;sendToAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;largest-contentful-paint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; lcp&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visibilitychange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note, this example waits until the user leaves the tab to report LCP as a way
of ensuring it only reports the latest entry. If you would prefer to report
every entry (to avoid potentially missing sessions), make sure to configure
your analytics to only include the last entry received per page load.&lt;/p&gt;
&lt;h2 id=&quot;what-if-the-largest-element-isn&#39;t-the-most-important&quot;&gt;What if the largest element isn&#39;t the most important? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-if-the-largest-element-isn&#39;t-the-most-important&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In some cases the most important element (or elements) on the page is not the
same as the largest element, and developers may be more interested in measuring
the render times of these other elements instead. This is possible using the
&lt;a href=&quot;https://wicg.github.io/element-timing/&quot;&gt;Element Timing API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Largest Contentful Paint API is actually built on top of the Element Timing
API and adds automatic reporting of the largest contentful element, but you can
report on additional elements by explicitly adding the &lt;code&gt;elementtiming&lt;/code&gt;
attribute to them, and registering a &lt;code&gt;PerformanceObserver&lt;/code&gt; to observe the
&lt;code&gt;element&lt;/code&gt; entry type.&lt;/p&gt;
&lt;p&gt;Here&#39;s the &lt;a href=&quot;https://wicg.github.io/element-timing/#sec-example&quot;&gt;example&lt;/a&gt;
used in the specification:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img...&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;elementtiming&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;foobar&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;elementtiming&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;important-paragraph&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This is text I care about.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; perfEntries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Process the entries by iterating over them.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;element&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffered&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; the &lt;a href=&quot;#what-elements-are-considered&quot;&gt;types of elements&lt;/a&gt;
considered for Largest Contentful Paint are the same as those observable via
the Element Timing API. If you add the &lt;code&gt;elementtiming&lt;/code&gt; attribute to an
element that isn&#39;t one of those types, it will be ignored.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;how-to-improve-largest-contentful-paint-on-your-site&quot;&gt;How to improve Largest Contentful Paint on your site &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-improve-largest-contentful-paint-on-your-site&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LCP is primarily affected by three factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server response time&lt;/li&gt;
&lt;li&gt;CSS blocking time&lt;/li&gt;
&lt;li&gt;Asset/subresource load time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, if your site is client-rendered, and your largest contentful elements are
added to the DOM via JavaScript, then your script&#39;s parse, compile, and
execution time can also be a factor in LCP.&lt;/p&gt;
&lt;p&gt;Here is some guidance you can reference to optimize all of these factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/apply-instant-loading-with-prpl&quot;&gt;Apply instant loading with the PRPL
pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/&quot;&gt;Optimizing the Critical Rendering
Path&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/fast#optimize-your-css&quot;&gt;Optimize your CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/fast#optimize-your-images&quot;&gt;Optimize your Images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/fast#optimize-web-fonts&quot;&gt;Optimize web Fonts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/fast#optimize-your-javascript&quot;&gt;Optimize you JavaScript&lt;/a&gt; (for
client-rendered sites)&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Native lazy-loading for the web</title>
    <link href="https://web.dev/native-lazy-loading/"/>
    <updated>2019-08-05T17:00:00-07:00</updated>
    <id>https://web.dev/native-lazy-loading/</id>
    <content type="html">&lt;p&gt;Support for natively lazy-loading images and iframes is coming to the web! This video shows
a &lt;a href=&quot;https://mathiasbynens.be/demo/img-loading-lazy&quot;&gt;demo&lt;/a&gt; of the feature:&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://web.dev/native-lazy-loading/lazyload.webm&quot; type=&quot;video/webm&quot;&gt;
    &lt;source src=&quot;https://web.dev/native-lazy-loading/lazyload.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;Starting with Chrome 76, you&#39;ll be able to use the new &lt;code&gt;loading&lt;/code&gt; attribute to lazy-load resources
without the need to write custom lazy-loading code or use a separate JavaScript library. Let&#39;s dive
into the details.&lt;/p&gt;
&lt;h2 id=&quot;why-native-lazy-loading&quot;&gt;Why native lazy-loading? &lt;a class=&quot;w-headline-link&quot; href=&quot;#why-native-lazy-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://httparchive.org/reports/page-weight&quot;&gt;HTTPArchive&lt;/a&gt;, images are the most
requested asset type for most websites and usually take up more bandwidth than any other
resource. At the 90th percentile, sites send about 4.7 MB of images on desktop and mobile. That&#39;s a
lot of &lt;a href=&quot;https://en.wikipedia.org/wiki/Cats_and_the_Internet&quot;&gt;cat pictures&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Embedded iframes also use a lot of data and can harm page performance. Only loading
non-critical, below-the-fold images and iframes when the user is likely to see them improves
page load times, minimizes user bandwidth, and reduces memory usage.&lt;/p&gt;
&lt;p&gt;Currently, there are two ways to defer the loading of off-screen images and iframes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the &lt;a href=&quot;https://developers.google.com/web/updates/2016/04/intersectionobserver&quot;&gt;Intersection Observer
API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;scroll&lt;/code&gt;, &lt;code&gt;resize&lt;/code&gt;, or &lt;code&gt;orientationchange&lt;/code&gt; &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/#using_event_handlers_the_most_compatible_way&quot;&gt;event
handlers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Either option can let developers include lazy-loading functionality, and many developers have built
third-party libraries to provide abstractions that are even easier to use. With lazy-loading
supported directly by the browser, however, there&#39;s no need for an external library. Native lazy
loading also ensures that deferred loading of images and iframes still works even if JavaScript is
disabled on the client.&lt;/p&gt;
&lt;h2 id=&quot;the-loading-attribute&quot;&gt;The &lt;code&gt;loading&lt;/code&gt; attribute &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-loading-attribute&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Today, Chrome already loads images at different priorities depending on where they&#39;re located with
respect to the device viewport. Images below the viewport are loaded with a lower priority, but they&#39;re
still fetched as soon as possible.&lt;/p&gt;
&lt;p&gt;In Chrome 76, you can use the &lt;code&gt;loading&lt;/code&gt; attribute to completely defer the loading of offscreen images
and iframes that can be reached by scrolling:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;iframe&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here are the supported values for the &lt;code&gt;loading&lt;/code&gt; attribute:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;: Default lazy-loading behavior of the browser, which is the same as not
including the attribute.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt;: Defer loading of the resource until it reaches a &lt;a href=&quot;#load-in-distance-threshold&quot;&gt;calculated distance&lt;/a&gt; from the viewport.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eager&lt;/code&gt;: Load the resource immediately, regardless of where it&#39;s located on the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The feature will continue to be updated until it&#39;s launched in a stable release (Chrome 76 at the
earliest). But you can try it out by enabling the following flags in Chrome:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#enable-lazy-image-loading&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://flags/#enable-lazy-frame-loading&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;load-in-distance-threshold&quot;&gt;Load-in distance threshold &lt;a class=&quot;w-headline-link&quot; href=&quot;#load-in-distance-threshold&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All images and iframes that are above the fold—that is, immediately viewable without scrolling—load
normally. Those that are far below the device viewport are only fetched when the user scrolls near
them.&lt;/p&gt;
&lt;p&gt;The distance threshold is not fixed and varies depending on several factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The type of resource being fetched (image or iframe)&lt;/li&gt;
&lt;li&gt;Whether &lt;a href=&quot;https://blog.chromium.org/2019/04/data-saver-is-now-lite-mode.html&quot;&gt;Lite mode&lt;/a&gt; is enabled
on Chrome for Android&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://googlechrome.github.io/samples/network-information/&quot;&gt;effective connection type&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the default values for the different effective connection types in the &lt;a href=&quot;https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/frame/settings.json5?l=971-1003&amp;amp;rcl=e8f3cf0bbe085fee0d1b468e84395aad3ebb2cad&quot;&gt;Chromium
source&lt;/a&gt;.
These numbers, and even the approach of fetching only when a certain distance from the viewport is
reached, may change in the near future as the Chrome team improves heuristics to determine when to
begin loading.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;In Chrome 77, you can experiment with these different thresholds by &lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/network/#throttle&quot;&gt;throttling the
network&lt;/a&gt; in DevTools. In
the meantime, you will need to override the effective connection type of the browser using the
&lt;code&gt;chrome://flags/#force-effective-connection-type&lt;/code&gt; flag.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;image-loading&quot;&gt;Image loading &lt;a class=&quot;w-headline-link&quot; href=&quot;#image-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To prevent the surrounding content from reflowing when a lazy-loaded image is downloaded, make sure
to add &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt; attributes to the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element or specify their values directly in an
inline style:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style-attr language-css&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt; &lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Images will still lazy-load if dimensions are not included, but specifying them decreases the chance of
browser reflow.&lt;/p&gt;
&lt;p&gt;Support for the &lt;code&gt;intrinsicsize&lt;/code&gt; attribute is also being &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=967992&quot;&gt;worked
on&lt;/a&gt;, so images will lazy-load
correctly if &lt;code&gt;intrinsicsize&lt;/code&gt; is specified along with one other dimension (&lt;code&gt;width&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;intrinsicsize&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;250x200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;450&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- lazy-loaded --&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Take a look at this &lt;a href=&quot;https://mathiasbynens.be/demo/img-loading-lazy&quot;&gt;demo&lt;/a&gt; to see how the &lt;code&gt;loading&lt;/code&gt; attribute works with 100 pictures.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;iframe-loading&quot;&gt;iframe loading &lt;a class=&quot;w-headline-link&quot; href=&quot;#iframe-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute affects iframes differently than images, depending on whether the iframe is
hidden. (Hidden iframes are often used for analytics or communication purposes.) Chrome uses the
following criteria to determine whether an iframe is hidden:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The iframe&#39;s width and height are 4 px or smaller.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display: none&lt;/code&gt; or &lt;code&gt;visibility: hidden&lt;/code&gt; is applied.&lt;/li&gt;
&lt;li&gt;The iframe is placed off-screen using negative X or Y positioning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If an iframe meets any of these conditions, Chrome considers it hidden and won&#39;t lazy-load it in
most cases. Iframes that &lt;em&gt;aren&#39;t&lt;/em&gt; hidden will only load when they&#39;re within the &lt;a href=&quot;#load-in-distance-threshold&quot;&gt;load-in distance threshold&lt;/a&gt;.
A placeholder shows for lazy-loaded iframes that are still being fetched.&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;FAQ &lt;a class=&quot;w-headline-link&quot; href=&quot;#faq&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;are-there-plans-to-expand-this-feature&quot;&gt;Are there plans to expand this feature? &lt;a class=&quot;w-headline-link&quot; href=&quot;#are-there-plans-to-expand-this-feature&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are plans to change the default lazy-loading behavior of the browser to automatically
lazy-load any images or iframes that are well suited to being deferred if &lt;a href=&quot;https://blog.chromium.org/2019/04/data-saver-is-now-lite-mode.html&quot;&gt;Lite
mode&lt;/a&gt; is enabled on Chrome for
Android.&lt;/p&gt;
&lt;h3 id=&quot;can-i-change-how-close-an-image-or-iframe-needs-to-be-before-a-load-is-triggered&quot;&gt;Can I change how close an image or iframe needs to be before a load is triggered? &lt;a class=&quot;w-headline-link&quot; href=&quot;#can-i-change-how-close-an-image-or-iframe-needs-to-be-before-a-load-is-triggered&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These values are hardcoded and can&#39;t be changed through the API. However, they may change in the
future as the Chrome team experiments with different threshold distances and variables.&lt;/p&gt;
&lt;h3 id=&quot;can-css-background-images-take-advantage-of-the-loading-attribute&quot;&gt;Can CSS background images take advantage of the &lt;code&gt;loading&lt;/code&gt; attribute? &lt;a class=&quot;w-headline-link&quot; href=&quot;#can-css-background-images-take-advantage-of-the-loading-attribute&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;No, it can currently only be used with &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
&lt;h3 id=&quot;how-does-the-loading-attribute-work-with-images-that-are-in-the-viewport-but-not-immediately-visible-(for-example-behind-a-carousel)&quot;&gt;How does the &lt;code&gt;loading&lt;/code&gt; attribute work with images that are in the viewport but not immediately visible (for example, behind a carousel)? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-does-the-loading-attribute-work-with-images-that-are-in-the-viewport-but-not-immediately-visible-(for-example-behind-a-carousel)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Only images that are below the device viewport by the &lt;a href=&quot;#load-in-distance-threshold&quot;&gt;calculated
distance&lt;/a&gt; load lazily. All images above the viewport, regardless of
whether they&#39;re immediately visible, load normally.&lt;/p&gt;
&lt;h3 id=&quot;what-if-i&#39;m-already-using-a-third-party-library-or-a-script-to-lazy-load-images-or-iframes&quot;&gt;What if I&#39;m already using a third-party library or a script to lazy-load images or iframes? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-if-i&#39;m-already-using-a-third-party-library-or-a-script-to-lazy-load-images-or-iframes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute should not affect code that currently lazy-loads your assets in any way, but
there are a few important things to consider:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If your custom lazy-loader attempts to load images or frames sooner than when Chrome loads them
normally—that is, at a distance greater than the &lt;a href=&quot;#load-in-distance-threshold&quot;&gt;load-in distance threshold&lt;/a&gt;—
they are still deferred and load based on normal browser behavior.&lt;/li&gt;
&lt;li&gt;If your custom lazy-loader uses a shorter distance to determine when to load a particular image
or iframe than the browser, then the behavior would conform to your custom settings.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One of the important reasons to continue to use a third-party library along with &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; is
to provide a polyfill for browsers that do not yet support the attribute.&lt;/p&gt;
&lt;h3 id=&quot;do-other-browsers-support-native-lazy-loading&quot;&gt;Do other browsers support native lazy-loading? &lt;a class=&quot;w-headline-link&quot; href=&quot;#do-other-browsers-support-native-lazy-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute can be treated as a progressive enhancement. Browsers that support it can
lazy-load images and iframes. Those that don&#39;t yet can load images just like they would today. In
terms of cross-browser support, &lt;code&gt;loading&lt;/code&gt; should be supported in Chrome 76 and any Chromium 76-based
browsers. There is also &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1542784&quot;&gt;an open implementation bug for
Firefox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://w3c.github.io/web-performance/specs/ResourcePriorities/Overview.html&quot;&gt;similar API&lt;/a&gt; was
proposed and used in IE and Edge but was focused on lowering the download priorities of resources
instead of deferring them entirely. It was discontinued in favour of &lt;a href=&quot;https://w3c.github.io/resource-hints/&quot;&gt;resource
hints&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-handle-browsers-that-don&#39;t-yet-support-native-lazy-loading&quot;&gt;How do I handle browsers that don&#39;t yet support native lazy-loading? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-do-i-handle-browsers-that-don&#39;t-yet-support-native-lazy-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a polyfill or use a third-party library to lazy-load images on your site. The &lt;code&gt;loading&lt;/code&gt;
property can be used to detect if the feature is supported in the browser:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;loading&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLImageElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// supported in browser&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// fetch polyfill/third-party library&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, &lt;a href=&quot;https://github.com/aFarkas/lazysizes&quot;&gt;lazysizes&lt;/a&gt; is a popular JavaScript lazy-loading
library. You can detect support for the &lt;code&gt;loading&lt;/code&gt; attribute to load lazysizes as a fallback
library only when &lt;code&gt;loading&lt;/code&gt; isn&#39;t supported. This works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;img src&amp;gt;&lt;/code&gt; with &lt;code&gt;&amp;lt;img data-src&amp;gt;&lt;/code&gt; to avoid an eager load in unsupported browsers. If the
&lt;code&gt;loading&lt;/code&gt; attribute is supported, swap &lt;code&gt;data-src&lt;/code&gt; for &lt;code&gt;src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;loading&lt;/code&gt; is not supported, load a fallback (lazysizes) and initiate it. As per lazysizes docs, you use the
&lt;code&gt;lazyload&lt;/code&gt; class as a way to indicate to lazysizes which images to lazy-load.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Let&#39;s load this in-viewport image normally --&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hero.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Let&#39;s lazy-load the rest of these images --&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;unicorn.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cats.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dogs.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;loading&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLImageElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; images &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;img[loading=&quot;lazy&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    images&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Dynamically import the LazySizes library&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; script &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;script&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    script&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&#39;https://cdnjs.cloudflare.com/ajax/libs/lazysizes/4.1.8/lazysizes.min.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s a &lt;a href=&quot;https://lazy-loading.firebaseapp.com/lazy_loading_native.html&quot;&gt;demo&lt;/a&gt; of this pattern. Try
it out in a browser like Firefox or Safari to see the fallback in action.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;The lazysizes library also provides a &lt;a href=&quot;https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/native-loading&quot;&gt;native loading plugin&lt;/a&gt;
that uses native lazy-loading when available but falls back to the library&#39;s custom functionality when needed.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;how-does-native-lazy-loading-affect-advertisements-on-a-web-page&quot;&gt;How does native lazy-loading affect advertisements on a web page? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-does-native-lazy-loading-affect-advertisements-on-a-web-page&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All ads displayed to the user in the form of an image or iframe lazy-load just
like any other image or iframe.&lt;/p&gt;
&lt;h3 id=&quot;how-are-images-handled-when-a-web-page-is-printed&quot;&gt;How are images handled when a web page is printed? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-are-images-handled-when-a-web-page-is-printed&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although the functionality isn&#39;t in Chrome 76, there&#39;s an &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=875403&quot;&gt;open
issue&lt;/a&gt; to ensure that all images and
iframes are immediately loaded if a page is printed.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Baking in native support for lazy-loading images and iframes can make it significantly easier for
you to improve the performance of your web pages.&lt;/p&gt;
&lt;p&gt;Are you noticing any unusual behavior with this feature enabled in Chrome? &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/entry?summary=%5BLazyLoad%5D:&amp;amp;comment=Application%20Version%20%28from%20%22Chrome%20Settings%20%3E%20About%20Chrome%22%29:%20%0DAndroid%20Build%20Number%20%28from%20%22Android%20Settings%20%3E%20About%20Phone/Tablet%22%29:%20%0DDevice:%20%0D%0DSteps%20to%20reproduce:%20%0D%0DObserved%20behavior:%20%0D%0DExpected%20behavior:%20%0D%0DFrequency:%20%0D%3Cnumber%20of%20times%20you%20were%20able%20to%20reproduce%3E%20%0D%0DAdditional%20comments:%20%0D&amp;amp;labels=Pri-2&amp;amp;components=Blink%3ELoader%3ELazyLoad%2C&quot;&gt;File a
bug&lt;/a&gt;!&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Establish network connections early to improve perceived page speed</title>
    <link href="https://web.dev/preconnect-and-dns-prefetch/"/>
    <updated>2019-07-29T17:00:00-07:00</updated>
    <id>https://web.dev/preconnect-and-dns-prefetch/</id>
    <content type="html">&lt;p&gt;Before the browser can request a resource from a server, it must establish a connection. Establishing a secure connection involves three steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Look up the domain name and resolve it to an IP address.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up a connection to the server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Encrypt the connection for security.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each of these steps the browser sends a piece of data to a server, and the server sends back a response. That journey, from origin to destination and back, is called a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Round_Trip_Time_(RTT)&quot;&gt;round trip&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Depending on network conditions, a single round trip might take a significant amount of time. The connection setup process might involve up to three round trips—and more in unoptimized cases.&lt;/p&gt;
&lt;p&gt;Taking care of all that ahead of time makes applications feel much faster. This post explains how to achieve that with two resource hints: &lt;code&gt;&amp;lt;link rel=preconnect&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;link rel=dns-prefetch&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;establish-early-connections-with-relpreconnect&quot;&gt;Establish early connections with &lt;code&gt;rel=preconnect&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#establish-early-connections-with-relpreconnect&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Modern browsers &lt;a href=&quot;https://www.igvita.com/posa/high-performance-networking-in-google-chrome/#tcp-pre-connect&quot;&gt;try their best to anticipate&lt;/a&gt; what connections a page will need, but they cannot reliably predict them all. The good news is that you can give them a (resource 😉) hint.&lt;/p&gt;
&lt;p&gt;Adding &lt;code&gt;rel=preconnect&lt;/code&gt; to a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; informs the browser that your page intends to establish a connection to another domain, and that you&#39;d like the process to start as soon as possible. Resources will load more quickly because the setup process has already been completed by the time the browser requests them.&lt;/p&gt;
&lt;p&gt;Resource hints get their name because they are not mandatory instructions. They provide the information about what you&#39;d like to happen, but it&#39;s ultimately up to the browser to decide whether to execute them. Setting up and keeping a connection open is a lot of work, so the browser might choose to ignore resource hints or execute them partially depending on the situation.&lt;/p&gt;
&lt;p&gt;Informing the browser of your intention is as simple as adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to your page:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/preconnect-and-dns-prefetch/preconnect.png&quot; alt=&quot;A diagram showing how the download doesn&#39;t start for a while after the connection is established.&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can speed up the load time by 100–500 ms by establishing early connections to important third-party origins. These numbers might seem small, but they make a difference in how &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/rail#ux&quot;&gt;users perceive web page performance&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;chrome.com &lt;a href=&quot;https://twitter.com/addyosmani/status/1090874825286000640&quot;&gt;improved Time To Interactive&lt;/a&gt; by almost 1 s by pre-connecting to important origins.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;use-cases-for-relpreconnect&quot;&gt;Use-cases for &lt;code&gt;rel=preconnect&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#use-cases-for-relpreconnect&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;knowing-where-from-but-not-what-you&#39;re-fetching&quot;&gt;Knowing &lt;em&gt;where from&lt;/em&gt;, but not &lt;em&gt;what&lt;/em&gt; you&#39;re fetching &lt;a class=&quot;w-headline-link&quot; href=&quot;#knowing-where-from-but-not-what-you&#39;re-fetching&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Due to versioned dependencies, you sometimes end up in a situation where you know you&#39;ll be requesting a resource from a particular CDN, but not the exact path for it.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
&lt;img src=&quot;https://web.dev/preconnect-and-dns-prefetch/versioned-url.png&quot; style=&quot;max-width:450px&quot; alt=&quot;A url of a script with the version name.&quot;&gt;
&lt;figcaption&gt;An example of a versioned URL.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The other common case is loading images from an &lt;a href=&quot;https://web.dev/image-cdns&quot;&gt;image CDN&lt;/a&gt;, where the exact path for an image depends on media queries or runtime feature checks on the user&#39;s browser.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
&lt;img src=&quot;https://web.dev/preconnect-and-dns-prefetch/image-cdn-url.png&quot; alt=&quot;An image CDN URL with the parameters size=300x400 and quality=auto.&quot;&gt;
&lt;figcaption&gt;An example of an image CDN URL.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In these situations, if the resource you&#39;ll be fetching is important, you want to save as much time as possible by pre-connecting to the server. The browser won&#39;t download the file until your page requests it, but at least it can handle the connection aspects ahead of time, saving the user from waiting for several round trips.&lt;/p&gt;
&lt;h3 id=&quot;streaming-media&quot;&gt;Streaming media &lt;a class=&quot;w-headline-link&quot; href=&quot;#streaming-media&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another example where you may want to save some time in the connection phase, but not necessarily start retrieving content right away, is when streaming media from a different origin.&lt;/p&gt;
&lt;p&gt;Depending on how your page handles the streamed content, you may want to wait until your scripts have loaded and are ready to process the stream. Pre-connecting helps you cut the waiting time to a single round trip once you&#39;re ready to start fetching.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-relpreconnect&quot;&gt;How to implement &lt;code&gt;rel=preconnect&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-implement-relpreconnect&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One way of initiating a &lt;code&gt;preconnect&lt;/code&gt; is adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preconnecting is only effective for domains other than the origin domain, so you shouldn&#39;t use it for your site.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
Only preconnect to critical domains you will use soon because the browser closes any connection that isn&#39;t used within 10 seconds. Unnecessary preconnecting can delay other important resources, so limit the number of preconnected domains and &lt;a href=&quot;https://andydavies.me/blog/2019/08/07/experimenting-with-link-rel-equals-preconnect-using-custom-script-injection-in-webpagetest/&quot;&gt;test the impact preconnecting makes&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can also initiate a preconnect via the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link&quot;&gt;&lt;code&gt;Link&lt;/code&gt; HTTP header&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Link: &amp;lt;https://example.com/&amp;gt;; rel=preconnect&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;A benefit of specifying a preconnect hint in the HTTP header is that it doesn&#39;t rely on markup being parsed, and it can be triggered by requests for stylesheets, scripts, and more. For example, Google Fonts sends a &lt;code&gt;Link&lt;/code&gt; header in the stylesheet response to preconnect to the domain that hosts the font files.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Some types of resources, such as fonts, are loaded in &lt;a href=&quot;https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements&quot;&gt;anonymous mode&lt;/a&gt;. For those you must set the &lt;code&gt;crossorigin&lt;/code&gt; attribute with the &lt;code&gt;preconnect&lt;/code&gt; hint:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com/ComicSans&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you omit the &lt;code&gt;crossorigin&lt;/code&gt; attribute, the browser only performs the DNS lookup.&lt;/p&gt;
&lt;h2 id=&quot;resolve-domain-name-early-with-reldns-prefetch&quot;&gt;Resolve domain name early with &lt;code&gt;rel=dns-prefetch&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#resolve-domain-name-early-with-reldns-prefetch&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You remember sites by their names, but servers remember them by IP addresses. This is why the domain name system (DNS) exists. The browser uses DNS to convert the site name to an IP address. This process — &lt;a href=&quot;https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/&quot;&gt;domain name resolution&lt;/a&gt;— is the first step in establishing a connection.&lt;/p&gt;
&lt;p&gt;If a page needs to make connections to many third-party domains, preconnecting all of them is counterproductive. The &lt;code&gt;preconnect&lt;/code&gt; hint is best used for only the most critical connections. For all the rest, use  &lt;code&gt;&amp;lt;link rel=dns-prefetch&amp;gt;&lt;/code&gt; to save time on the first step, the DNS lookup, which usually takes around &lt;a href=&quot;https://www.keycdn.com/support/reduce-dns-lookups&quot;&gt;20–120 ms&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;DNS resolution is initiated similarly to &lt;code&gt;preconnect&lt;/code&gt;: by adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dns-prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://caniuse.com/#search=dns-prefetch&quot;&gt;Browser support for &lt;code&gt;dns-prefetch&lt;/code&gt;&lt;/a&gt; is slightly different from &lt;a href=&quot;https://caniuse.com/#search=preconnect&quot;&gt;&lt;code&gt;preconnect&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;https://caniuse.com/#search=preconnect&quot;&gt;support&lt;/a&gt;, so &lt;code&gt;dns-prefetch&lt;/code&gt; can serve as a fallback for browsers that don&#39;t support &lt;code&gt;preconnect&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dns-prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--better&quot;&gt;
    Recommended
  &lt;/span&gt;
  &lt;p&gt;—
  To safely implement the fallback technique use separate link tags.&lt;/p&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect dns-prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--worse&quot;&gt;
    Not recommended
  &lt;/span&gt;
  &lt;p&gt;—
  Implementing &lt;code&gt;dns-prefetch&lt;/code&gt; fallback in the same &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag causes a bug in Safari where &lt;code&gt;preconnect&lt;/code&gt; gets cancelled.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These two resource hints are helpful for improving page speed when you know you&#39;ll download something from a third-party domain soon, but you don&#39;t know the exact URL for the resource. Examples include CDNs that distribute JavaScript libraries, images or fonts. Be mindful of constraints, use &lt;code&gt;preconnect&lt;/code&gt; only for the most important resources, rely on &lt;code&gt;dns-prefetch&lt;/code&gt; for the rest, and always measure the impact in the real-world.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Create OS-style backgrounds with backdrop-filter</title>
    <link href="https://web.dev/backdrop-filter/"/>
    <updated>2019-07-25T17:00:00-07:00</updated>
    <id>https://web.dev/backdrop-filter/</id>
    <content type="html">&lt;p&gt;Translucence, blurring, and other effects are useful ways of creating depth while keeping the context of the background content. They support a host of use cases such as frosted glass, video overlays, translucent navigation headers, inappropriate image censoring, image loading, and so on. You may recognize these effects from two popular operating systems: &lt;a href=&quot;https://i.kinja-img.com/gawker-media/image/upload/s--9RLXARU4--/c_scale,dpr_2.0,f_auto,fl_progressive,q_80,w_800/trgz8yivyyqrpcnwscu5.png&quot;&gt;Windows 10&lt;/a&gt; and &lt;a href=&quot;https://static.businessinsider.com/image/51fd2822eab8eae16e00000b-750.jpg&quot;&gt;iOS&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/backdrop-filter/weather_app.jpg&quot; alt=&quot;An example of a frosted glass effect.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    An example of a frosted glass effect. &lt;a href=&quot;https://dribbble.com/shots/733714-Weather-App?list=tags&amp;tag=android&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Historically, these techniques were difficult to implement on the web, requiring less than perfect &lt;a href=&quot;https://stackoverflow.com/questions/38145368/css-workaround-to-backdrop-filter&quot;&gt;hacks or workarounds&lt;/a&gt;. In recent years both &lt;a href=&quot;https://webkit.org/blog/3632/introducing-backdrop-filters/&quot;&gt;Safari&lt;/a&gt; and Edge have provided these capabilities through the &lt;code&gt;background-filter&lt;/code&gt; (and alternatively, the &lt;code&gt;-webkit-backdrop-filter&lt;/code&gt;) property, which dynamically blends foreground and background colors based on filter functions. Now Chrome supports &lt;code&gt;background-filter&lt;/code&gt;, starting in version 76.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/backdrop-filter/backdrop_filter-kitchen_sink.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    A demonstration of the filter functions for &lt;code&gt;backdrop-filter&lt;/code&gt;. Try the example on &lt;a href=&quot;https://codepen.io/robinrendle/pen/LmzLEL&quot; target=&quot;_blank&quot;&gt;CodePen&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;basics&quot;&gt;Basics &lt;a class=&quot;w-headline-link&quot; href=&quot;#basics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;backdrop-filter&lt;/code&gt; property applies one or more filters to an element, changing the appearance of anything behind the element.&lt;/li&gt;
&lt;li&gt;The overlaying element must be at least partially transparent.&lt;/li&gt;
&lt;li&gt;The overlaying element will get a new stacking context.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
&lt;code&gt;backdrop-filter&lt;/code&gt; may harm performance. Test it before deploying.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;CSS &lt;code&gt;backdrop-filter&lt;/code&gt; applies one or more effects to an element that is translucent or transparent. To understand that, consider the images below.&lt;/p&gt;
&lt;div class=&quot;w-columns&quot;&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--worse&quot;&gt;
    No foreground transparency
  &lt;/span&gt;
  &lt;p&gt;—
  &lt;img src=&quot;https://web.dev/backdrop-filter/transparency-off.png&quot; alt=&quot;A triangle superimposed on a circle. The circle can&#39;t be seen through the triangle.&quot;&gt;&lt;/p&gt;
  &lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.frosty-glass-pane {
    backdrop-filter: blur(2px);
  }
  &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--better&quot;&gt;
    Foreground transparency
  &lt;/span&gt;
  &lt;p&gt;—
  &lt;img src=&quot;https://web.dev/backdrop-filter/transparency-on.png&quot; alt=&quot;A triangle superimposed on a circle. The triangle is translucent, allowing the circle to be seen through it.&quot;&gt;&lt;/p&gt;
  &lt;pre&gt;&lt;code class=&quot;language-css/1&quot;&gt;.frosty-glass-pane {
    opacity: .9;
    backdrop-filter: blur(2px);
  }
  &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The image on the left shows how overlapping elements would be rendered if &lt;code&gt;backdrop-filter&lt;/code&gt; were not used or supported. The image on the right applies a blurring effect using &lt;code&gt;backdrop-filter&lt;/code&gt;. Notice that it uses &lt;code&gt;opacity&lt;/code&gt; in addition to &lt;code&gt;backdrop-filter&lt;/code&gt;. Without &lt;code&gt;opacity&lt;/code&gt;, there would be nothing to apply blurring to. It almost goes without saying that if &lt;code&gt;opacity&lt;/code&gt; is set to &lt;code&gt;1&lt;/code&gt; (fully opaque) there will be no effect on the background.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;backdrop-filter&lt;/code&gt; property is like CSS &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/filter&quot;&gt;filters&lt;/a&gt; in that all your favorite &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/filter#Filter_functions&quot;&gt;filter functions&lt;/a&gt; are supported: &lt;code&gt;blur()&lt;/code&gt;, &lt;code&gt;brightness()&lt;/code&gt;, &lt;code&gt;contrast()&lt;/code&gt;, &lt;code&gt;opacity()&lt;/code&gt;, &lt;code&gt;drop-shadow()&lt;/code&gt;, and so on. It also supports the &lt;code&gt;url()&lt;/code&gt; function if you want to use an external image as the filter, as well as the keywords &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;inherit&lt;/code&gt;, &lt;code&gt;initial&lt;/code&gt;, and &lt;code&gt;unset&lt;/code&gt;. There are explanations for all of this on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter&quot;&gt;MDN&lt;/a&gt;, including descriptions of syntax, filters, and values.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;backdrop-filter&lt;/code&gt; is set to anything other than &lt;code&gt;none&lt;/code&gt;, the browser creates a new &lt;a href=&quot;https://www.w3.org/TR/CSS21/zindex.html&quot;&gt;stacking context&lt;/a&gt;. A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block&quot;&gt;containing block&lt;/a&gt; may also be created, but only if the element has absolute and fixed position descendants.&lt;/p&gt;
&lt;p&gt;You can combine filters for rich and clever effects, or use just one filter for more subtle or precise effects. You can even combine them with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter&quot;&gt;SVG filters&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;feature-detection-and-fallback&quot;&gt;Feature detection and fallback &lt;a class=&quot;w-headline-link&quot; href=&quot;#feature-detection-and-fallback&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As with many features of the modern web, you&#39;ll want to know whether the user&#39;s browser supports &lt;code&gt;backdrop-filter&lt;/code&gt; before using it. Do this with &lt;code&gt;@supports()&lt;/code&gt;. For performance reasons, fall back to an image instead of a polyfill when &lt;code&gt;backdrop-image&lt;/code&gt; isn&#39;t supported. The example below shows this.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;	&lt;span class=&quot;token selector&quot;&gt;.background&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;		&lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;10px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; not &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token selector&quot;&gt;.background&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blurred-hero.png&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples &lt;a class=&quot;w-headline-link&quot; href=&quot;#examples&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Design techniques and styles previously reserved for native operating systems are now performant and achievable with a single CSS declaration. Let&#39;s look at some examples.&lt;/p&gt;
&lt;h3 id=&quot;single-filter&quot;&gt;Single filter &lt;a class=&quot;w-headline-link&quot; href=&quot;#single-filter&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the following example, the frosted effect is achieved by combining color and blur. The blur is supplied by &lt;code&gt;backdrop-filter&lt;/code&gt;, while the color comes from the element&#39;s semi-transparent background color.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.blur-behind-me&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;.5rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/backdrop-filter/backdrop_filter-rgb.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Try this example for yourself in &lt;a href=&quot;https://codepen.io/netsi1964/pen/JqBLPK&quot; target=&quot;_blank&quot;&gt;CodePen&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;multiple-filters&quot;&gt;Multiple filters &lt;a class=&quot;w-headline-link&quot; href=&quot;#multiple-filters&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes you&#39;ll need multiple filters to achieve the desired effect. To do this, provide a list of filters separated by a space. For example:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.brighten-saturate-and-blur-behind-me&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;brightness&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;150%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saturate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;150%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the following example, each of the four panes has a different combination of backdrop filters while the same set of shapes are animated behind them.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/backdrop-filter/backdrop_filter-ambient_blur.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Try this example for yourself in &lt;a href=&quot;https://codepen.io/pepf/pen/GqZkdj&quot; target=&quot;_blank&quot;&gt;CodePen&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;overlays&quot;&gt;Overlays &lt;a class=&quot;w-headline-link&quot; href=&quot;#overlays&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This example shows how to blur a semi-transparent background to make text readable while stylistically blending with a page&#39;s background.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.modal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;10px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 255&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/backdrop-filter/backdrop_filter-modal.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Try this &lt;a href=&quot;https://mfreed7.github.io/backdrop-filter-feature/examples/scrollable.html&quot; target=&quot;_blank&quot;&gt;example&lt;/a&gt; for yourself.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;text-contrast-on-dynamic-backgrounds&quot;&gt;Text contrast on dynamic backgrounds &lt;a class=&quot;w-headline-link&quot; href=&quot;#text-contrast-on-dynamic-backgrounds&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As stated earlier, &lt;code&gt;backdrop-filter&lt;/code&gt; allows performant effects that would be difficult or impossible on the web. An example of this is changing a background in respone to an animation. In this example, &lt;code&gt;backdrop-filter&lt;/code&gt; maintains the high contrast between the text and its background in spite of what&#39;s going on behind the text. It starts with the default background color &lt;code&gt;darkslategray&lt;/code&gt; and uses &lt;code&gt;backdrop-filter&lt;/code&gt; to invert the colors after the transformation.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; darkslategray&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;	&lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/backdrop-filter/backdrop_filter-invert_color.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Try this example from &lt;a href=&quot;https://www.chenhuijing.com/#%F0%9F%91%9F&quot;&gt;Chen Hui Jing&lt;/a&gt; in &lt;a href=&quot;https://tympanus.net/codrops-playground/huijing/Qqpwg5Iy/editor&quot; target=&quot;_blank&quot;&gt;Codrops&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;More than 560 of you have upvoted the &lt;a href=&quot;https://crbug.com/497522&quot;&gt;Chromium bug&lt;/a&gt; over the past few years, clearly marking this as a long awaited CSS feature. Chrome&#39;s release of &lt;code&gt;backdrop-filter&lt;/code&gt; in version 76 brings the web a step closer to truly native-like UI presentation.&lt;/p&gt;
&lt;h3 id=&quot;additional-resources&quot;&gt;Additional resources &lt;a class=&quot;w-headline-link&quot; href=&quot;#additional-resources&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty&quot;&gt;Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chromestatus.com/feature/5679432723333120&quot;&gt;Chrome Platform Status&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter&quot;&gt;MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/the-backdrop-filter-css-property/&quot;&gt;&lt;code&gt;background-filter&lt;/code&gt; at CSS Tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codepen.io/tag/backdrop-filter/#&quot;&gt;Samples on Codepen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Keeping things fresh with stale-while-revalidate</title>
    <link href="https://web.dev/stale-while-revalidate/"/>
    <updated>2019-07-17T17:00:00-07:00</updated>
    <id>https://web.dev/stale-while-revalidate/</id>
    <content type="html">&lt;h2 id=&quot;what-shipped&quot;&gt;What shipped? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-shipped&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc5861#section-3&quot;&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt;&lt;/a&gt; helps
developers balance between immediacy—&lt;em&gt;loading cached content right away&lt;/em&gt;—and
freshness—&lt;em&gt;ensuring updates to the cached content are used in the future&lt;/em&gt;. If
you maintain a third-party web service or library that updates on a regular
schedule, or your first-party assets tend to have short lifetimes, then
&lt;code&gt;stale-while-revalidate&lt;/code&gt; may be a useful addition to your existing caching
policies.&lt;/p&gt;
&lt;p&gt;Support for setting &lt;code&gt;stale-while-revalidate&lt;/code&gt; alongside &lt;code&gt;max-age&lt;/code&gt; in your
&lt;code&gt;Cache-Control&lt;/code&gt; response header is available in &lt;a href=&quot;https://chromestatus.com/feature/5050913014153216&quot;&gt;Chrome 75&lt;/a&gt;
and &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1536511&quot;&gt;Firefox 68&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Browsers that don&#39;t support &lt;code&gt;stale-while-revalidate&lt;/code&gt; will silently ignore that
configuration value, and use
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#max-age&quot;&gt;&lt;code&gt;max-age&lt;/code&gt;&lt;/a&gt;,
as I&#39;ll explain shortly…&lt;/p&gt;
&lt;h2 id=&quot;what&#39;s-it-mean&quot;&gt;What&#39;s it mean? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what&#39;s-it-mean&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s break down &lt;code&gt;stale-while-revalidate&lt;/code&gt; into two parts: the idea that a cached
response might be stale, and the process of revalidation.&lt;/p&gt;
&lt;p&gt;First, how does the browser know whether a cached response is &amp;quot;stale&amp;quot;? A
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control&quot;&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt;
response header that contains &lt;code&gt;stale-while-revalidate&lt;/code&gt; should also contain
&lt;code&gt;max-age&lt;/code&gt;, and the number of seconds specified via &lt;code&gt;max-age&lt;/code&gt; is what determines
staleness. Any cached response newer than &lt;code&gt;max-age&lt;/code&gt; is considered fresh, and
older cached responses are stale.&lt;/p&gt;
&lt;p&gt;If the locally cached response is still fresh, then it can be used as-is to
fulfill a browser&#39;s request. From the perspective of &lt;code&gt;stale-while-revalidate&lt;/code&gt;,
there&#39;s nothing to do in this scenario.&lt;/p&gt;
&lt;p&gt;But if the cached response is stale, then another age-based check is performed:
is the age of the cached response within the window of time covered by the
&lt;code&gt;stale-while-revalidate&lt;/code&gt; setting?&lt;/p&gt;
&lt;p&gt;If the age of a stale response falls into this window, then it will be used to
fulfill the browser&#39;s request. At the same time, a &amp;quot;revalidation&amp;quot; request will
be made against the network in a way that doesn&#39;t delay the use of the cached
response. The returned response might contain the same information as the
previously cached response, or it might be different. Either way, the network
response is stored locally, replacing whatever was previously cache, and
resetting the &amp;quot;freshness&amp;quot; timer used during any future &lt;code&gt;max-age&lt;/code&gt; comparisons.&lt;/p&gt;
&lt;p&gt;However, if the stale cached response is old enough that it falls outside the
&lt;code&gt;stale-while-revalidate&lt;/code&gt; window of time, then it will not fulfill the browser&#39;s
request. The browser will instead retrieve a response from the network, and use
that for both fulfilling the initial request and also populating the local cache
with a fresh response.&lt;/p&gt;
&lt;h2 id=&quot;live-example&quot;&gt;Live Example &lt;a class=&quot;w-headline-link&quot; href=&quot;#live-example&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Below is a simple example of an HTTP API for returning the current time—more
specifically, the current number of minutes past the hour.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 346px; width: 100%;&quot;&gt;
  &lt;iframe src=&quot;https://glitch.com/embed/#!/embed/s-w-r-demo?path=server.js:20:15&amp;previewSize=100&amp;attributionHidden=true&quot; alt=&quot;Stale-while-revalidate Demo on Glitch&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;In this scenario, the web server uses this &lt;code&gt;Cache-Control&lt;/code&gt; header in its HTTP response:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Cache-Control: max-age=1, stale-while-revalidate=59&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This setting means that, if a request for the time is repeated within the next 1
second, the previously cached value will still be fresh, and used as-is, without
any revalidation.&lt;/p&gt;
&lt;p&gt;If a request is repeated between 1 and 60 seconds later, then the cached value
will be stale, but will be used to fulfill the API request. At the same time,
&amp;quot;in the background,&amp;quot; a revalidation request will be made to populate the cache
with a fresh value for future use.&lt;/p&gt;
&lt;p&gt;If a request is repeated after more than 60 seconds, then the stale response
isn&#39;t used at all, and both fulfilling the browser&#39;s request and the cache
revalidation will depend on getting a response back from the network.&lt;/p&gt;
&lt;p&gt;Here&#39;s a breakdown of those three distinct states, along with the window of time
in which each of them apply for our example:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/stale-while-revalidate/s-w-r-diagram.svg&quot; alt=&quot;A diagram illustrating the information from the previous section.&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-are-the-common-use-cases&quot;&gt;What are the common use cases? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-are-the-common-use-cases&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While the above example for a &amp;quot;minutes after the hour&amp;quot; API service is contrived,
it illustrates the expected use case—services that provide information which
needs to be refreshed, but where some degree of staleness is acceptable.&lt;/p&gt;
&lt;p&gt;Less contrived examples might be an API for the current weather conditions, or
the top news headlines that were written in the past hour.&lt;/p&gt;
&lt;p&gt;Generally, any response that updates at a known interval, is likely to be
requested multiple times, and is static within that interval is a good candidate
for short-term caching via &lt;code&gt;max-age&lt;/code&gt;. Using &lt;code&gt;stale-while-revalidate&lt;/code&gt; in addition
to &lt;code&gt;max-age&lt;/code&gt; increases the likelihood that future requests can be fulfilled from
the cache with fresher content, without blocking on a network response.&lt;/p&gt;
&lt;h2 id=&quot;how-does-it-interact-with-service-workers&quot;&gt;How does it interact with service workers? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-does-it-interact-with-service-workers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;ve heard of &lt;code&gt;stale-while-revalidate&lt;/code&gt; chances are that it was in the
context of
&lt;a href=&quot;https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate&quot;&gt;recipes&lt;/a&gt;
used within a &lt;a href=&quot;https://web.dev/service-workers-cache-storage/&quot;&gt;service worker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using stale-while-revalidate via a &lt;code&gt;Cache-Control&lt;/code&gt; header shares some
similarities with its use in a service worker, and many of the same
considerations around freshness trade-offs and maximum lifetimes apply. However,
there are a few considerations that you should take into account when deciding
whether to implement a service worker-based approach, or just rely on the
&lt;code&gt;Cache-Control&lt;/code&gt; header configuration.&lt;/p&gt;
&lt;h3 id=&quot;use-a-service-worker-approach-if...&quot;&gt;Use a service worker approach if… &lt;a class=&quot;w-headline-link&quot; href=&quot;#use-a-service-worker-approach-if...&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You&#39;re already using a service worker in your web app.&lt;/li&gt;
&lt;li&gt;You need fine-grained control over the contents of your caches, and want to
implement something like a least-recently used expiration policy. Workbox&#39;s
&lt;a href=&quot;https://developers.google.com/web/tools/workbox/modules/workbox-cache-expiration&quot;&gt;Cache Expiration&lt;/a&gt;
module can help with this.&lt;/li&gt;
&lt;li&gt;You want to be notified when a stale response changes in the background during
the revalidation step. Workbox&#39;s
&lt;a href=&quot;https://developers.google.com/web/tools/workbox/modules/workbox-broadcast-cache-update&quot;&gt;Broadcast Cache Update&lt;/a&gt;
module can help with this.&lt;/li&gt;
&lt;li&gt;You need this &lt;code&gt;stale-while-revalidate&lt;/code&gt; behavior in all modern browsers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-a-cache-control-approach-if...&quot;&gt;Use a Cache-Control approach if… &lt;a class=&quot;w-headline-link&quot; href=&quot;#use-a-cache-control-approach-if...&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You would rather not deal with the overhead of deploying and maintaining a
service worker for your web app.&lt;/li&gt;
&lt;li&gt;You are fine with letting the browser&#39;s automatic cache management prevent
your local caches from growing too large.&lt;/li&gt;
&lt;li&gt;You are fine with an approach that is not currently supported in all modern
browsers (as of July 2019; support may grow in the future).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;re using a service worker and also have &lt;code&gt;stale-while-revalidate&lt;/code&gt; enabled
for some responses via a &lt;code&gt;Cache-Control&lt;/code&gt; header, then the service worker will,
in general, have &amp;quot;first crack&amp;quot; at responding to a request. If the service worker
decides not to respond, or if in the process of generating a response it makes a
network request using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;&lt;code&gt;fetch()&lt;/code&gt;&lt;/a&gt;,
then the behavior configured via the &lt;code&gt;Cache-Control&lt;/code&gt; header will end up going
into effect.&lt;/p&gt;
&lt;h2 id=&quot;learn-more&quot;&gt;Learn more &lt;a class=&quot;w-headline-link&quot; href=&quot;#learn-more&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://fetch.spec.whatwg.org/#concept-stale-while-revalidate-response&quot;&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt; response&lt;/a&gt;
in the Fetch API spec.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc5861&quot;&gt;RFC 5861&lt;/a&gt;, covering the initial
&lt;code&gt;stale-while-revalidate&lt;/code&gt; specification.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;The HTTP cache: your first line of defense&lt;/a&gt;, from the &amp;quot;Network
reliability&amp;quot; guide on this site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Hero image by Samuel Zeller on &lt;a href=&quot;https://unsplash.com/photos/oBb-Y26PJgg&quot;&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Faster web navigation with predictive prefetching</title>
    <link href="https://web.dev/predictive-prefetching/"/>
    <updated>2019-07-07T17:00:00-07:00</updated>
    <id>https://web.dev/predictive-prefetching/</id>
    <content type="html">&lt;p&gt;In my &lt;a href=&quot;https://www.youtube.com/watch?v=0jB4YWgAxUo&quot;&gt;Faster Web Navigation with Predictive Prefetching&lt;/a&gt; session at Google I/O 2019, I began by talking about optimizing web apps with code-splitting and the potential performance implications for subsequent page navigation. In the second part of the talk, I discussed how to improve navigation speed by using Guess.js to set up predictive prefetching:&lt;/p&gt;
&lt;div class=&quot;w-youtube&quot;&gt;
  &lt;iframe class=&quot;w-youtube__embed&quot; src=&quot;https://www.youtube.com/embed/0jB4YWgAxUo&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;code-splitting-for-faster-web-apps&quot;&gt;Code-splitting for faster web apps &lt;a class=&quot;w-headline-link&quot; href=&quot;#code-splitting-for-faster-web-apps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web apps are slow, and JavaScript is among the most expensive resources that you ship. Waiting for a slow web app to load can frustrate your users and decrease conversions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/predictive-prefetching/guess-0.png&quot; alt=&quot;Slow web apps are stressful.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Lazy-loading is an efficient technique to reduce the bytes of JavaScript that you&#39;re transferring over the wire. You can use several techniques to lazy-load JavaScript, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Component-level code-splitting&lt;/li&gt;
&lt;li&gt;Route-level code-splitting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With component-level code-splitting, you can move individual components into separate JavaScript chunks. On particular events, you can load the relevant scripts and render the components.&lt;/p&gt;
&lt;p&gt;With route-level code-splitting, however, you move entire &lt;em&gt;routes&lt;/em&gt; into independent chunks. When users transition from one route to another, they have to download the associated JavaScript and bootstrap the requested page. These operations can lead to significant delays, especially on slow networks.&lt;/p&gt;
&lt;h2 id=&quot;prefetching-javascript&quot;&gt;Prefetching JavaScript &lt;a class=&quot;w-headline-link&quot; href=&quot;#prefetching-javascript&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Prefetching allows the browser to download and cache resources that the user is likely to need soon. The usual method is to use &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt;, but there are two common pitfalls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefetching too many resources (&lt;em&gt;overfetching&lt;/em&gt;) consumes a lot of data.&lt;/li&gt;
&lt;li&gt;Some resources the user needs may never be prefetched.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Predictive prefetching solves these problems by using a report of users&#39; navigational patterns to determine what assets to prefetch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/predictive-prefetching/guess-1.png&quot; alt=&quot;Prefetching example&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;predictive-prefetching-with-guess.js&quot;&gt;Predictive prefetching with Guess.js &lt;a class=&quot;w-headline-link&quot; href=&quot;#predictive-prefetching-with-guess.js&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/guess-js&quot;&gt;Guess.js&lt;/a&gt; is a JavaScript library that provides predictive prefetching functionality. Guess.js consumes a report from Google Analytics or another analytics provider to build a predictive model that can be used to smartly prefetch only what the user is likely to need.&lt;/p&gt;
&lt;p&gt;Guess.js has integrations with &lt;a href=&quot;https://angular.io/&quot;&gt;Angular&lt;/a&gt;, &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt;, &lt;a href=&quot;https://nuxtjs.org/&quot;&gt;Nuxt.js&lt;/a&gt;, and &lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt;. To use it in your application, add these lines to your webpack configuration to specify a &lt;a href=&quot;https://stackoverflow.com/questions/36898103/what-is-a-viewid-in-google-analytics&quot;&gt;Google Analytics view ID&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; GuessPlugin &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;guess-webpack&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;   &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GuessPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;GA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;XXXXXX&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re not using Google Analytics, you can specify a &lt;code&gt;reportProvider&lt;/code&gt; and download data from your favorite service.&lt;/p&gt;
&lt;h3 id=&quot;integration-with-frameworks&quot;&gt;Integration with frameworks &lt;a class=&quot;w-headline-link&quot; href=&quot;#integration-with-frameworks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To learn more about how to integrate Guess.js with your favorite framework check out these resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://guess-js.github.io/docs/angular&quot;&gt;Using Guess.js with Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guess-js.github.io/docs/next&quot;&gt;Using Guess.js with Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guess-js.github.io/docs/nuxt&quot;&gt;Using Guess.js with Nuxt.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a quick walkthrough on the integration with Angular, check out this video:&lt;/p&gt;
&lt;div class=&quot;w-youtube&quot;&gt;
  &lt;iframe class=&quot;w-youtube__embed&quot; src=&quot;https://www.youtube.com/embed/5FRxQiGqqmM&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;how-does-guess.js-work&quot;&gt;How does Guess.js work? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-does-guess.js-work&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here&#39;s how Guess.js implements predictive prefetching:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It first extracts data for the user navigational patterns from your favorite analytics provider.&lt;/li&gt;
&lt;li&gt;It then maps the URLs from the report to the JavaScript chunks produced by webpack.&lt;/li&gt;
&lt;li&gt;Based on the extracted data, it creates a simple predictive model of which pages a user is likely to navigate to from any given page.&lt;/li&gt;
&lt;li&gt;It invokes the model for each JavaScript chunk, predicting the other chunks that are likely to be needed next.&lt;/li&gt;
&lt;li&gt;It adds prefetching instructions to each chunk.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When Guess.js is done, each chunk will contain prefetching instructions similar to:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;__GUESS__&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;a.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;b.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This Guess.js-generated code is telling the browser to consider prefetching chunk &lt;code&gt;a.js&lt;/code&gt; with probability &lt;code&gt;0.2&lt;/code&gt; and chunk &lt;code&gt;b.js&lt;/code&gt; with probability &lt;code&gt;0.8&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once the browser executes the code, Guess.js will check the user&#39;s connection speed. If it&#39;s sufficient, Guess.js will insert two &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tags in the header of the page, one for each chunk. If the user is on a high-speed network, Guess.js will prefetch both chunks. If the user has a poor network connection, Guess.js will only prefetch chunk &lt;code&gt;b.js&lt;/code&gt; since it has a high probability of being needed.&lt;/p&gt;
&lt;h2 id=&quot;learn-more&quot;&gt;Learn more &lt;a class=&quot;w-headline-link&quot; href=&quot;#learn-more&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To learn more about Guess.js, check out these resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=0jB4YWgAxUo&quot;&gt;Faster Web Navigation with Predictive Prefetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.mgechev.com/2018/05/09/introducing-guess-js-data-driven-user-experiences-web/&quot;&gt;Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guess-js.github.io/&quot;&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/guess-js&quot;&gt;Source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How OpenSooq increased engagement by investing in the web</title>
    <link href="https://web.dev/opensooq-case-study/"/>
    <updated>2019-07-02T17:00:00-07:00</updated>
    <id>https://web.dev/opensooq-case-study/</id>
    <content type="html">&lt;p&gt;Based in Amman, Jordan, &lt;a href=&quot;http://jo.opensooq.com/en&quot;&gt;OpenSooq&lt;/a&gt; is a mobile-first classifieds marketplace offering a wide range of products and services in 19 countries across the Middle East and North Africa.&lt;/p&gt;
&lt;h2 id=&quot;the-challenge&quot;&gt;The challenge &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-challenge&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Over 85% of OpenSooq&#39;s traffic comes from mobile devices, and that number continues to rise. Many people in the region rely on low-end devices with limited storage capacities, which creates a strong need for OpenSooq&#39;s mobile site to be as fast and light as possible. However, slow load times on OpenSooq&#39;s old mobile site were affecting customer satisfaction and led to &lt;a href=&quot;https://support.google.com/analytics/answer/1009409?hl=en--for&quot;&gt;bounce rates&lt;/a&gt; as high as 49% in markets like Kuwait.&lt;/p&gt;
&lt;p&gt;To serve its customers with a consistently better experience across all platforms, the company realized it needed a faster, more responsive website. So, in June 2017, OpenSooq&#39;s engineering team launched a &lt;a href=&quot;https://web.dev/discover-installable/&quot;&gt;Progressive Web App (PWA)&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;a-focus-on-performance-and-reliability&quot;&gt;A focus on performance and reliability &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-focus-on-performance-and-reliability&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenSooq&#39;s three in-house developers were able to build a full-featured PWA with &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; and &lt;a href=&quot;https://webpack.js.org/&quot;&gt;webpack&lt;/a&gt; in just two and a half months.&lt;/p&gt;
&lt;p&gt;To ensure that the site would be fast and easily indexable by search engines, the team chose to implement server-side rendering. With almost 28% of their users accessing the PWA on patchy 2G or 3G networks, it was critical that users trust the experience to work, regardless of network constraints. So the team implemented an offline experience using &lt;a href=&quot;https://web.dev/service-workers-cache-storage&quot;&gt;service workers&lt;/a&gt; and a &lt;a href=&quot;https://developers.google.com/web/tools/workbox/modules/workbox-strategies#cache_first_cache_falling_back_to_network&quot;&gt;cache-first-then-network strategy&lt;/a&gt;. They also used the &lt;a href=&quot;https://web.dev/apply-instant-loading-with-prpl/?hl=en&quot;&gt;PRPL pattern&lt;/a&gt; to give their users instant loading.&lt;/p&gt;
&lt;p&gt;By adopting these best practices, the team was able to cut the average page load time (&lt;a href=&quot;https://web.dev/interactive&quot;&gt;Time to Interactive&lt;/a&gt;) from 4 seconds to less than 2 seconds. And they used &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; to make sure the site &lt;em&gt;stayed&lt;/em&gt; that fast. Those efforts helped OpenSooq hit over 1.8 billion pageviews a month.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/opensooq-case-study/lh-performance.png&quot; alt=&quot;A screenshot of the Lighthouse performance metrics for the OpenSooq PWA.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    OpenSooq&#39;s Lighthouse performance metrics.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote class=&quot;w-blockquote&quot;&gt;
  &lt;p class=&quot;w-blockquote__text&quot;&gt;
    Over a quarter of our users access the OpenSooq PWA from low-mid networks, so reliability was essential for keeping those users engaged. Service workers and caching strategies helped us build the reliable user experience we needed, seamlessly.
  &lt;/p&gt;
  &lt;cite class=&quot;w-blockquote__cite&quot;&gt;
    Amin Shoman, PWA Technical Manager, OpenSooq
  &lt;/cite&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;improving-re-engagement&quot;&gt;Improving re-engagement &lt;a class=&quot;w-headline-link&quot; href=&quot;#improving-re-engagement&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class=&quot;w-figure w-figure--inline-right&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/opensooq-case-study/notifications.png&quot; alt=&quot;A smartphone displaying OpenSooq notifications.&quot; style=&quot;max-width: 284px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    OpenSooq&#39;s meaningful user notifications.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Having built a performant and reliable experience, the OpenSooq team wanted to make sure their users stayed engaged with the product. To do that, they enabled users to install the &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/promoting-install-mobile&quot;&gt;PWA to their home screen&lt;/a&gt; and added support for &lt;a href=&quot;https://developers.google.com/web/fundamentals/push-notifications/&quot;&gt;meaningful notifications&lt;/a&gt;. This allowed the site to notify buyers about seller interaction on queries and sellers about ad activation and expiration.&lt;/p&gt;
&lt;p&gt;These improvements increased the monthly active users of OpenSooq&#39;s web experience by 14%. For users who installed the PWA, there was a 48% increase in the number of page visits per session and a 28% jump in average session duration. OpenSooq also saw a 25% increase in users&#39; average time on a page and a 29% drop in average bounce rates across all markets.&lt;/p&gt;
&lt;h2 id=&quot;looking-ahead&quot;&gt;Looking ahead &lt;a class=&quot;w-headline-link&quot; href=&quot;#looking-ahead&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After just a few weeks of effort, OpenSooq created a 23 KB, full-featured PWA that delivers a consistent experience to users across all platforms and browsers. And because it&#39;s easy to update features on the web, the team has gone PWA-first by launching and testing new features and user flows on their PWA before rolling them out to other platforms.&lt;/p&gt;
&lt;p&gt;OpenSooq is now working on adding a &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/native&quot;&gt;native install prompt&lt;/a&gt; and integrating the PWA with &lt;a href=&quot;https://amp.dev/&quot;&gt;accelerated mobile pages (AMP)&lt;/a&gt;. These enhancements will further speed up page loads and make the transition to the native app as seamless as possible. By building on the foundation of their initial development effort, the OpenSooq team will continue giving their users the best possible experience.&lt;/p&gt;
&lt;blockquote class=&quot;w-blockquote&quot;&gt;
  &lt;p class=&quot;w-blockquote__text&quot;&gt;
    The importance of developing our PWA was clear to both the leadership and technical teams. After building a modern web app and making it performant, we&#39;re excited to see the business impact on our 2.5 month investment.
  &lt;/p&gt;
  &lt;cite class=&quot;w-blockquote__cite&quot;&gt;
    Ramzi Alqrainy, Chief Technology Officer, OpenSooq
  &lt;/cite&gt;
&lt;/blockquote&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Hello darkness, my old friend</title>
    <link href="https://web.dev/prefers-color-scheme/"/>
    <updated>2019-06-26T17:00:00-07:00</updated>
    <id>https://web.dev/prefers-color-scheme/</id>
    <content type="html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;w-headline-link&quot; href=&quot;#introduction&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;📚 I have done a lot of background research on the history and theory of dark mode,
if you are only interested in working with dark mode, feel free to
&lt;a href=&quot;#activating-dark-mode-in-the-operating-system&quot;&gt;skip the introduction&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;dark-mode-before-dark-mode&quot;&gt;Dark mode before &lt;em&gt;Dark Mode&lt;/em&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-before-dark-mode&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure class=&quot;w-figure w-figure--inline-right&quot;&gt;
  &lt;img style=&quot;height: 175px; width: auto;&quot; src=&quot;https://web.dev/prefers-color-scheme/green-screen.jpg&quot; alt=&quot;Green screen computer monitor&quot; intrinsicsize=&quot;640x480&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Green screen (&lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Compaq_Portable_and_Wordperfect.JPG&quot;&gt;Source&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We have gone full circle with dark mode.
In the dawn of personal computing, dark mode wasn&#39;t a matter of choice,
but a matter of fact:
Monochrome &lt;abbr title=&quot;Cathode-Ray Tube&quot;&gt;CRT&lt;/abbr&gt; computer monitors worked by firing electron beams
on a phosphorescent screen and the phosphor used in early CRTs was green.
Because text was displayed in green and the rest of the screen was black, these models were often referred to as
&lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Schneider_CPC6128_with_green_monitor_GT65,_start_screen.jpg&quot;&gt;green screens&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--inline-left&quot;&gt;
  &lt;img style=&quot;height: 175px; width: auto;&quot; src=&quot;https://web.dev/prefers-color-scheme/word-processing.jpg&quot; alt=&quot;Dark-on-white word processing&quot; intrinsicsize=&quot;698x551&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Dark-on-white (&lt;a href=&quot;https://www.youtube.com/watch?v=qKkABzt0Zqg&quot;&gt;Source&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The subsequently introduced Color CRTs displayed multiple colors
through the use of red, green, and blue phosphors.
They created white by activating all three phosphors simultaneously.
With the advent of more sophisticated &lt;abbr title=&quot;What You See Is What You Get&quot;&gt;WYSIWYG&lt;/abbr&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Desktop_publishing&quot;&gt;desktop publishing&lt;/a&gt;,
the idea of making the virtual document resemble a physical sheet of paper became popular.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--inline-right&quot;&gt;
  &lt;img style=&quot;height: 175px; width: auto;&quot; src=&quot;https://web.dev/prefers-color-scheme/worldwideweb.png&quot; alt=&quot;Dark-on-white webpage in the WorldWideWeb browser&quot; intrinsicsize=&quot;1024x768&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;The WorldWideWeb browser (&lt;a href=&quot;https://commons.wikimedia.org/wiki/File:WorldWideWeb_FSF_GNU.png&quot;&gt;Source&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is where &lt;em&gt;dark-on-white&lt;/em&gt; as a design trend started,
and this trend was carried over to the
&lt;a href=&quot;http://info.cern.ch/hypertext/WWW/TheProject.html&quot;&gt;early document-based web&lt;/a&gt;.
The first ever browser,
&lt;a href=&quot;https://en.wikipedia.org/wiki/WorldWideWeb&quot;&gt;WorldWideWeb&lt;/a&gt;
(remember,
&lt;a href=&quot;https://en.wikipedia.org/wiki/Cascading_Style_Sheets#History&quot;&gt;CSS wasn&#39;t even invented&lt;/a&gt; yet),
&lt;a href=&quot;https://commons.wikimedia.org/wiki/File:WorldWideWeb_FSF_GNU.png&quot;&gt;displayed webpages&lt;/a&gt; this way.
Fun fact: the second ever browser,
&lt;a href=&quot;https://en.wikipedia.org/wiki/Line_Mode_Browser&quot;&gt;Line Mode Browser&lt;/a&gt;—a terminal-based browser—was
green on dark.
These days, web pages and web apps are typically designed with dark text
on a light background, a baseline assumption that is also hard-coded in user agent stylesheets, including
&lt;a href=&quot;https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css&quot;&gt;Chrome&#39;s&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--inline-left&quot;&gt;
  &lt;img style=&quot;height: 175px; width: auto;&quot; src=&quot;https://web.dev/prefers-color-scheme/smartphone-in-bed.jpg&quot; alt=&quot;Smartphone used while lying in bed&quot; intrinsicsize=&quot;500x334&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Smartphone used in bed (&lt;a href=&quot;https://unsplash.com/photos/W39xsPWZgA4&quot;&gt;Source&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The days of CRTs are long over.
Content consumption and creation has shifted to mobile devices
that use backlit &lt;abbr title=&quot;Liquid Crystal Display&quot;&gt;LCD&lt;/abbr&gt;
or energy-saving &lt;abbr title=&quot;Active-Matrix Organic Light-Emitting Diode&quot;&gt;AMOLED&lt;/abbr&gt; screens.
Smaller and more transportable computers, tablets, and smartphones led to new usage patterns.
Leisure tasks like web browsing, coding for fun, and high-end gaming
frequently happen after-hours in dim environments.
People even enjoy their devices in their beds at night-time.
The more people use their devices in the dark,
the more the idea of going back to the roots of &lt;em&gt;light-on-dark&lt;/em&gt; becomes popular.&lt;/p&gt;
&lt;h3 id=&quot;why-dark-mode&quot;&gt;Why dark mode &lt;a class=&quot;w-headline-link&quot; href=&quot;#why-dark-mode&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;dark-mode-for-aesthetic-reasons&quot;&gt;Dark mode for aesthetic reasons &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-for-aesthetic-reasons&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When people get asked
&lt;a href=&quot;https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d&quot;&gt;why they like or want dark mode&lt;/a&gt;,
the most popular response is that &lt;em&gt;&amp;quot;it&#39;s easier on the eyes,&amp;quot;&lt;/em&gt;
followed by &lt;em&gt;&amp;quot;it&#39;s elegant and beautiful.&amp;quot;&lt;/em&gt;
Apple in their
&lt;a href=&quot;https://developer.apple.com/documentation/appkit/supporting_dark_mode_in_your_interface&quot;&gt;Dark Mode developer documentation&lt;/a&gt;
explicitly writes: &lt;em&gt;&amp;quot;The choice of whether to enable a light or dark appearance
is an aesthetic one for most users, and might not relate to ambient lighting conditions.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;👩‍🔬 Read up more on
&lt;a href=&quot;https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d&quot;&gt;user research regarding why people want dark mode and how they use it&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;figure class=&quot;w-figure w-figure--inline-right&quot;&gt;
  &lt;img style=&quot;height: 225px; width: auto;&quot; src=&quot;https://web.dev/prefers-color-scheme/closeview.png&quot; alt=&quot;CloseView in Mac OS System 7 with \&quot; White=&quot;&quot; on=&quot;&quot; Black\&quot;=&quot;&quot; mode&quot;=&quot;&quot; intrinsicsize=&quot;531x618&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;System&amp;nbsp;7 CloseView (&lt;a href=&quot;https://archive.org/details/mac_Macintosh_System_7_at_your_Fingertips_1992&quot;&gt;Source&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;dark-mode-as-an-accessibility-tool&quot;&gt;Dark mode as an accessibility tool &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-as-an-accessibility-tool&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are also people who actually &lt;em&gt;need&lt;/em&gt; dark mode and use it as another accessibility tool,
for example, users with low vision.
The earliest occurrence of such an accessibility tool I could find is
&lt;a href=&quot;https://en.wikipedia.org/wiki/System_7&quot;&gt;System 7&lt;/a&gt;&#39;s &lt;em&gt;CloseView&lt;/em&gt; feature, which had a toggle for
&lt;em&gt;Black on White&lt;/em&gt; and &lt;em&gt;White on Black&lt;/em&gt;.
While System 7 supported color, the default user interface was still black-and-white.&lt;/p&gt;
&lt;p&gt;These inversion-based implementations demonstrated their weaknesses once color was introduced.
User research by Szpiro &lt;em&gt;et al.&lt;/em&gt; on
&lt;a href=&quot;https://dl.acm.org/citation.cfm?id=2982168&quot;&gt;how people with low vision access computing devices&lt;/a&gt;
showed that all interviewed users disliked inverted images,
but that many preferred light text on a dark background.
Apple accommodates for this user preference with a feature called
&lt;a href=&quot;https://www.apple.com//accessibility/iphone/vision/&quot;&gt;Smart Invert&lt;/a&gt;,
which reverses the colors on the display, except for images, media,
and some apps that use dark color styles.&lt;/p&gt;
&lt;p&gt;A special form of low vision is Computer Vision Syndrome, also known as Digital Eye Strain, which is
&lt;a href=&quot;https://onlinelibrary.wiley.com/doi/full/10.1111/j.1475-1313.2011.00834.x&quot;&gt;defined&lt;/a&gt;
as &lt;em&gt;&amp;quot;the combination of eye and vision problems associated with the use of computers
(including desktop, laptop, and tablets) and other electronic displays (e.g.
smartphones and electronic reading devices).&amp;quot;&lt;/em&gt;
It has been &lt;a href=&quot;https://bmjopen.bmj.com/content/5/1/e006748&quot;&gt;proposed&lt;/a&gt;
that the use of electronic devices by adolescents, particularly at night time,
leads to an increased risk of shorter sleep duration,
longer sleep-onset latency, and increased sleep deficiency.
Additionally, exposure to blue light has been widely
&lt;a href=&quot;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4254760/&quot;&gt;reported&lt;/a&gt;
to be involved in the regulation of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Circadian_rhythm&quot;&gt;circadian rhythm&lt;/a&gt;
and the sleep cycle,
and irregular light environments may lead to sleep deprivation,
possibly affecting mood and task performance, according to
&lt;a href=&quot;https://www.college-optometrists.org/oip-resource/computer-vision-syndrome--a-k-a--digital-eye-strain.html&quot;&gt;research by Rosenfield&lt;/a&gt;.
To limit these negative effects, reducing blue light by adjusting the display color temperature
through features like iOS&#39; &lt;a href=&quot;https://support.apple.com/en-us/HT207570&quot;&gt;Night Shift&lt;/a&gt; or Android&#39;s
&lt;a href=&quot;https://support.google.com/pixelphone/answer/7169926?&quot;&gt;Night Light&lt;/a&gt; can help,
as well as avoiding bright lights or irregular lights in general through dark themes or dark modes.&lt;/p&gt;
&lt;h4 id=&quot;dark-mode-power-savings-on-amoled-screens&quot;&gt;Dark mode power savings on AMOLED screens &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-power-savings-on-amoled-screens&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Finally, dark mode is known to save a &lt;em&gt;lot&lt;/em&gt; of energy on
&lt;abbr title=&quot;Active-Matrix Organic Light-Emitting Diode&quot;&gt;AMOLED&lt;/abbr&gt; screens.
Android case studies that focused on popular Google apps
like YouTube have shown that the power savings can be up to 60%.
The video below has more details on these case studies and the power savings per app.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;div class=&quot;w-youtube&quot;&gt;
  &lt;iframe class=&quot;w-youtube__embed&quot; src=&quot;https://www.youtube.com/embed/N_6sPd0Jd3g?start=305&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;activating-dark-mode-in-the-operating-system&quot;&gt;Activating dark mode in the operating system &lt;a class=&quot;w-headline-link&quot; href=&quot;#activating-dark-mode-in-the-operating-system&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that I have covered the background of why dark mode is such a big deal for many users,
let&#39;s review how you can support it.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--inline-left&quot;&gt;
  &lt;img style=&quot;height: 250px; width: auto;&quot; src=&quot;https://web.dev/prefers-color-scheme/android.png&quot; alt=&quot;Android Q dark mode settings&quot; intrinsicsize=&quot;610x700&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Android&amp;nbsp;Q dark theme settings&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Operating systems that support a dark mode or dark theme
typically have an option to activate it somewhere in the settings.
On macOS X, it&#39;s in the system preference&#39;s &lt;em&gt;General&lt;/em&gt; section and called &lt;em&gt;Appearance&lt;/em&gt; (&lt;a href=&quot;https://web.dev/prefers-color-scheme/macosx.png&quot;&gt;screenshot&lt;/a&gt;),
and on Windows 10, it&#39;s in the &lt;em&gt;Colors&lt;/em&gt; section and called &lt;em&gt;Choose your color&lt;/em&gt; (&lt;a href=&quot;https://web.dev/prefers-color-scheme/windows10.png&quot;&gt;screenshot&lt;/a&gt;).
For Android Q, you can find it under &lt;em&gt;Display&lt;/em&gt; as a &lt;em&gt;Dark Theme&lt;/em&gt; toggle switch (&lt;a href=&quot;https://web.dev/prefers-color-scheme/android.png&quot;&gt;screenshot&lt;/a&gt;),
and on iOS 13, you can change the &lt;em&gt;Appearance&lt;/em&gt; in the &lt;em&gt;Display &amp;amp; Brightness&lt;/em&gt;
section of the settings (&lt;a href=&quot;https://web.dev/prefers-color-scheme/ios.jpg&quot;&gt;screenshot&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&quot;the-prefers-color-scheme-media-query&quot; class=&quot;w-clearfix&quot;&gt;The &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query&lt;/h2&gt;
&lt;p&gt;One last bit of theory before I get going.
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries&quot;&gt;Media queries&lt;/a&gt;
allow authors to test and query values or features of the user agent or display device,
independent of the document being rendered.
They are used in the CSS &lt;code&gt;@media&lt;/code&gt; rule to conditionally apply styles to a document,
and in various other contexts and languages, such as HTML and JavaScript.
&lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/&quot;&gt;Media Queries Level 5&lt;/a&gt;
introduces so-called user preference media features, that is,
a way for sites to detect the user&#39;s preferred way to display content.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;☝️ An established user preference media feature is &lt;code&gt;prefers-reduced-motion&lt;/code&gt;
that lets you detect the desire for less motion on a page.
I have
&lt;a href=&quot;https://developers.google.com/web/updates/2019/03/prefers-reduced-motion&quot;&gt;written about &lt;code&gt;prefers-reduced-motion&lt;/code&gt;&lt;/a&gt;
before.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme&quot;&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;
media feature is used to detect
if the user has requested the page to use a light or dark color theme.
It works with the following values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;no-preference&lt;/code&gt;:
Indicates that the user has made no preference known to the system.
This keyword value evaluates as &lt;code&gt;false&lt;/code&gt; in the
&lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#boolean-context&quot;&gt;boolean context&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;light&lt;/code&gt;:
Indicates that the user has notified the system that they prefer a page that has a light theme
(dark text on light background).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dark&lt;/code&gt;:
Indicates that the user has notified the system that they prefer a page that has a dark theme
(light text on dark background).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;supporting-dark-mode&quot;&gt;Supporting dark mode &lt;a class=&quot;w-headline-link&quot; href=&quot;#supporting-dark-mode&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;finding-out-if-dark-mode-is-supported-by-the-browser&quot;&gt;Finding out if dark mode is supported by the browser &lt;a class=&quot;w-headline-link&quot; href=&quot;#finding-out-if-dark-mode-is-supported-by-the-browser&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As dark mode is reported through a media query, you can easily check if the current browser
supports dark mode by checking if the media query &lt;code&gt;prefers-color-scheme&lt;/code&gt; matches at all.
Note how I don&#39;t include any value, but purely check if the media query alone matches.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(prefers-color-scheme)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;media &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;not all&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;🎉 Dark mode is supported&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the time of writing, &lt;code&gt;prefers-color-scheme&lt;/code&gt; is supported on both desktop and mobile (where available)
by Chrome and Edge as of version 76, Firefox as of version 67,
and Safari as of version 12.1 on macOS and as of version 13 on iOS.
For all other browsers, you can check the &lt;a href=&quot;https://caniuse.com/#feat=prefers-color-scheme&quot;&gt;Can I use support tables&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;There is a custom element &lt;a href=&quot;https://github.com/GoogleChromeLabs/dark-mode-toggle&quot;&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt;
available that adds dark mode support to older browsers.
I write about it &lt;a href=&quot;#the-lessdark-mode-togglegreater-custom-element&quot;&gt;further down in this article&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;dark-mode-in-practice&quot;&gt;Dark mode in practice &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-in-practice&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s finally see how supporting dark mode looks like in practice.
Just like with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Highlander_(film)&quot;&gt;Highlander&lt;/a&gt;,
with dark mode &lt;em&gt;there can be only one&lt;/em&gt;: dark or light, but never both!
Why do I mention this? Because this fact should have an impact on the loading strategy.
&lt;strong&gt;Please don&#39;t force users to download CSS in the critical rendering path
that is for a mode they don&#39;t currently use.&lt;/strong&gt;
To optimize load speed, I have therefore split my CSS for the example app
that shows the following recommendations in practice
into three parts in order to &lt;a href=&quot;https://web.dev/defer-non-critical-css/&quot;&gt;defer non-critical CSS&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;style.css&lt;/code&gt; that contains generic rules that are used universally on the site.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dark.css&lt;/code&gt; that contains only the rules needed for dark mode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;light.css&lt;/code&gt; that contains only the rules needed for light mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;loading-strategy&quot;&gt;Loading strategy &lt;a class=&quot;w-headline-link&quot; href=&quot;#loading-strategy&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The two latter ones, &lt;code&gt;light.css&lt;/code&gt; and &lt;code&gt;dark.css&lt;/code&gt;,
are loaded conditionally with a &lt;code&gt;&amp;lt;link media&amp;gt;&lt;/code&gt; query.
Initially,
&lt;a href=&quot;https://caniuse.com/#feat=prefers-color-scheme&quot;&gt;not all browsers will support &lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;
(detectable using the &lt;a href=&quot;#finding-out-if-dark-mode-is-supported-by-the-browser&quot;&gt;pattern above&lt;/a&gt;),
which I deal with dynamically by loading the default &lt;code&gt;light.css&lt;/code&gt; file
via a conditionally inserted &lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;/code&gt; element in a minuscule inline script
(light is an arbitrary choice, I could also have made dark the default fallback experience).
To avoid a &lt;a href=&quot;https://en.wikipedia.org/wiki/Flash_of_unstyled_content&quot;&gt;flash of unstyled content&lt;/a&gt;,
I hide the content of the page until &lt;code&gt;light.css&lt;/code&gt; has loaded.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// If `prefers-color-scheme` is not supported, fall back to light mode.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// In this case, light.css will be downloaded with `highest` priority.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(prefers-color-scheme)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;media &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;not all&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;beforeend&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/light.css&quot; onload=&quot;document.documentElement.style.display = ``&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  Conditionally either load the light or the dark stylesheet. The matching file&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  will be downloaded with `highest`, the non-matching file with `lowest`&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  priority. If the browser doesn&#39;t support `prefers-color-scheme`, the media&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  query is unknown and the files are downloaded with `lowest` priority (but&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  above I already force `highest` priority for my default light experience).&lt;/span&gt;&lt;br&gt;--&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/dark.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: dark)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/light.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: no-preference), (prefers-color-scheme: light)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- The main stylesheet --&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/style.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;stylesheet-architecture&quot;&gt;Stylesheet architecture &lt;a class=&quot;w-headline-link&quot; href=&quot;#stylesheet-architecture&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I make maximum use of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/var&quot;&gt;CSS variables&lt;/a&gt;,
this allows my generic &lt;code&gt;style.css&lt;/code&gt; to be, well, generic,
and all the light or dark mode customization happens in the two other files &lt;code&gt;dark.css&lt;/code&gt; and &lt;code&gt;light.css&lt;/code&gt;.
Below you can see an excerpt of the actual styles, but it should suffice to convey the overall idea.
I declare two variables, &lt;code&gt;-⁠-⁠color&lt;/code&gt; and &lt;code&gt;-⁠-⁠background-color&lt;/code&gt;
that essentially create a &lt;em&gt;dark-on-light&lt;/em&gt; and a &lt;em&gt;light-on-dark&lt;/em&gt; baseline theme.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* light.css: 👉 dark-on-light */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* dark.css: 👉 light-on-dark */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my &lt;code&gt;style.css&lt;/code&gt;, I then use these variables in the &lt;code&gt;body { … }&lt;/code&gt; rule.
As they are defined on the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:root&quot;&gt;&lt;code&gt;:root&lt;/code&gt; CSS pseudo-class&lt;/a&gt;—a
selector that in HTML represents the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element
and is identical to the selector &lt;code&gt;html&lt;/code&gt;, except that its specificity is
higher—they cascade down, which serves me for declaring global CSS variables.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* style.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light dark&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--background-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code sample above, you will probably have noticed a property
&lt;a href=&quot;https://drafts.csswg.org/css-color-adjust-1/#propdef-color-scheme&quot;&gt;&lt;code&gt;color-scheme&lt;/code&gt;&lt;/a&gt;
with the space-separated value &lt;code&gt;light dark&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt;
The &lt;code&gt;color-scheme&lt;/code&gt; property is still &lt;a href=&quot;https://crbug.com/925935&quot;&gt;in development&lt;/a&gt;
and it might not work as advertised, full support in Chrome will come later this year.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This tells the browser which color themes my app supports
and allows it to activate special variants of the user agent stylesheet,
which is useful to, for example, let the browser render form fields
with a dark background and light text, adjust the scrollbars,
or to enable a theme-aware highlight color.
The exact details of &lt;code&gt;color-scheme&lt;/code&gt; are specified in
&lt;a href=&quot;https://drafts.csswg.org/css-color-adjust-1/&quot;&gt;CSS Color Adjustment Module Level 1&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;🌒 Read up more on
&lt;a href=&quot;https://medium.com/dev-channel/what-does-dark-modes-supported-color-schemes-actually-do-69c2eacdfa1d&quot;&gt;what &lt;code&gt;color-scheme&lt;/code&gt; actually does&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Everything else is then just a matter of defining CSS variables
for things that matter on my site.
Semantically organizing styles helps a lot when working with dark mode.
For example, rather than &lt;code&gt;-⁠-⁠highlight-yellow&lt;/code&gt;, consider calling the variable
&lt;code&gt;-⁠-⁠accent-color&lt;/code&gt;, as &amp;quot;yellow&amp;quot; may actually not be yellow in dark mode or vice versa.
Below is an example of some more variables that I use in my example.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* dark.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--link-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 188&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 212&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--main-headline-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;233&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 30&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 99&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--accent-background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 188&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 212&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--accent-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* light.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--link-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 238&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--main-headline-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 192&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--accent-background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 238&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--accent-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 250&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;full-example&quot;&gt;Full example &lt;a class=&quot;w-headline-link&quot; href=&quot;#full-example&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the following &lt;a href=&quot;https://dark-mode-baseline.glitch.me/&quot;&gt;Glitch&lt;/a&gt; embed,
you can see the complete example that puts the concepts from above into practice.
Try toggling dark mode in your particular &lt;a href=&quot;#activating-dark-mode-in-the-operating-system&quot;&gt;operating system&#39;s settings&lt;/a&gt;
and see how the page reacts.&lt;/p&gt;
&lt;div style=&quot;height: 900px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; vr; encrypted-media&quot; src=&quot;https://glitch.com/embed/#!/embed/dark-mode-baseline?path=style.css&amp;previewSize=100&amp;attributionHidden=true&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;loading-impact&quot;&gt;Loading impact &lt;a class=&quot;w-headline-link&quot; href=&quot;#loading-impact&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When you play with this example, you can see
why I load my &lt;code&gt;dark.css&lt;/code&gt; and &lt;code&gt;light.css&lt;/code&gt; via media queries.
Try toggling dark mode and reload the page:
the particular currently non-matching stylesheets are still loaded, but with the lowest priority,
so that they never compete with resources that are needed by the site right now.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;😲 Read up more on
&lt;a href=&quot;https://blog.tomayac.com/2018/11/08/why-browsers-download-stylesheets-with-non-matching-media-queries-180513&quot;&gt;why browsers download stylesheets with non-matching media queries&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img src=&quot;https://web.dev/prefers-color-scheme/light.png&quot; alt=&quot;Network loading diagram showing how in light mode the dark mode CSS gets loaded with lowest priority&quot; intrinsicsize=&quot;1633x851&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Site in light mode loads the dark mode CSS with lowest priority.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img src=&quot;https://web.dev/prefers-color-scheme/dark.png&quot; alt=&quot;Network loading diagram showing how in dark mode the light mode CSS gets loaded with lowest priority&quot; intrinsicsize=&quot;1633x851&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Site in dark mode loads the light mode CSS with lowest priority.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img src=&quot;https://web.dev/prefers-color-scheme/unsupported.png&quot; alt=&quot;Network loading diagram showing how in default light mode the dark mode CSS gets loaded with lowest priority&quot; intrinsicsize=&quot;1633x851&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;Site in default light mode on a browser that doesn&#39;t support &lt;code&gt;prefers-color-scheme&lt;/code&gt; loads the dark mode CSS with lowest priority.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;reacting-on-dark-mode-changes&quot;&gt;Reacting on dark mode changes &lt;a class=&quot;w-headline-link&quot; href=&quot;#reacting-on-dark-mode-changes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Like any other media query change, dark mode changes can be subscribed to via JavaScript.
You can use this to, for example, dynamically change the
&lt;a href=&quot;https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/#provide_great_icons_tiles&quot;&gt;favicon&lt;/a&gt;
of a page or change the
&lt;a href=&quot;https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/#meta_theme_color_for_chrome_and_opera&quot;&gt;&lt;code&gt;&amp;lt;meta name=&amp;quot;theme-color&amp;quot;&amp;gt;&lt;/code&gt;&lt;/a&gt;
that determines the color of the URL bar in Chrome.
The &lt;a href=&quot;#full-example&quot;&gt;full example&lt;/a&gt; above shows this in action,
in order to see the theme color and favicon changes, open the
&lt;a href=&quot;https://dark-mode-baseline.glitch.me/&quot;&gt;demo in a separate tab&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; darkModeMediaQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(prefers-color-scheme: dark)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  darkModeMediaQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; darkModeOn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Dark mode is &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;darkModeOn &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;🌒 on&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;☀️ off&#39;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;dark-mode-best-practices&quot;&gt;Dark mode best practices &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-best-practices&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;avoid-pure-white&quot;&gt;Avoid pure white &lt;a class=&quot;w-headline-link&quot; href=&quot;#avoid-pure-white&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A small detail you may have noticed is that I don&#39;t use pure white.
Instead, to prevent glowing and bleeding against the surrounding dark content,
I choose a slightly darker white. Something like &lt;code&gt;rgb(250, 250, 250)&lt;/code&gt; works well.&lt;/p&gt;
&lt;h3 id=&quot;re-colorize-and-darken-photographic-images&quot;&gt;Re-colorize and darken photographic images &lt;a class=&quot;w-headline-link&quot; href=&quot;#re-colorize-and-darken-photographic-images&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you compare the two screenshots below, you will notice that not only the core theme has changed
from &lt;em&gt;dark-on-light&lt;/em&gt; to &lt;em&gt;light-on-dark&lt;/em&gt;, but that also the hero image looks slightly different.
My &lt;a href=&quot;https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b&quot;&gt;user research&lt;/a&gt;
has shown that the majority of the surveyed people
prefer slightly less vibrant and brilliant images when dark mode is active.
I refer to this as &lt;em&gt;re-colorization&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;w-columns&quot;&gt;
  &lt;figure class=&quot;w-figure&quot;&gt;
    &lt;img src=&quot;https://web.dev/prefers-color-scheme/hero-dark.png&quot; alt=&quot;Hero image slightly darkened in dark mode.&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
      Hero image slightly darkened in dark mode.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;w-figure&quot;&gt;
    &lt;img src=&quot;https://web.dev/prefers-color-scheme/hero-light.png&quot; alt=&quot;Regular hero image in light mode.&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
      Regular hero image in light mode.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Re-colorization can be achieved through a CSS filter on my images.
I use a CSS selector that matches all images that don&#39;t have &lt;code&gt;.svg&lt;/code&gt; in their URL,
the idea being that I can give vector graphics (icons) a different re-colorization treatment
than my images (photos), more about this in the &lt;a href=&quot;#vector-graphics-and-icons&quot;&gt;next paragraph&lt;/a&gt;.
Note how I again use a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/var&quot;&gt;CSS variable&lt;/a&gt;,
so I can later on flexibly change my filter.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;🎨 Read up more on
&lt;a href=&quot;https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b&quot;&gt;user research regarding re-colorization preferences with dark mode&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As re-colorization is only needed in dark mode, that is, when &lt;code&gt;dark.css&lt;/code&gt; is active,
there are no corresponding rules in &lt;code&gt;light.css&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* dark.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token property&quot;&gt;--image-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grayscale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token selector&quot;&gt;;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;img:not([src*=&quot;.svg&quot;])&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--image-filter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;customizing-dark-mode-re-colorization-intensities-with-javascript&quot;&gt;Customizing dark mode re-colorization intensities with JavaScript &lt;a class=&quot;w-headline-link&quot; href=&quot;#customizing-dark-mode-re-colorization-intensities-with-javascript&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Not everyone is the same and people have different dark mode needs.
By sticking to the re-colorization method described above,
I can easily make the grayscale intensity a user preference that I can
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties#Values_in_JavaScript&quot;&gt;change via JavaScript&lt;/a&gt;,
and by setting a value of &lt;code&gt;0%&lt;/code&gt;, I can also disable re-colorization completely.
Note that &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/documentElement&quot;&gt;&lt;code&gt;document.documentElement&lt;/code&gt;&lt;/a&gt;
provides a reference to the root element of the document,
that is, the same element I can reference with the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:root&quot;&gt;&lt;code&gt;:root&lt;/code&gt; CSS pseudo-class&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; filter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;grayscale(70%)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;--image-filter&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;invert-vector-graphics-and-icons&quot;&gt;Invert vector graphics and icons &lt;a class=&quot;w-headline-link&quot; href=&quot;#invert-vector-graphics-and-icons&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For vector graphics—that in my case are used as icons that I reference via &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements—I
use a different re-colorization method.
While &lt;a href=&quot;https://dl.acm.org/citation.cfm?id=2982168&quot;&gt;research&lt;/a&gt; has shown
that people don&#39;t like inversion for photos, it does work very well for most icons.
Again I use CSS variables to determine the inversion amount
in the regular and in the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:hover&quot;&gt;&lt;code&gt;:hover&lt;/code&gt;&lt;/a&gt; state.&lt;/p&gt;
&lt;div class=&quot;w-columns&quot;&gt;
  &lt;figure class=&quot;w-figure&quot;&gt;
    &lt;img src=&quot;https://web.dev/prefers-color-scheme/icons-dark.png&quot; alt=&quot;Icons are inverted in dark mode.&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
      Icons are inverted in dark mode.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;w-figure&quot;&gt;
    &lt;img src=&quot;https://web.dev/prefers-color-scheme/icons-light.png&quot; alt=&quot;Regular icons in light mode.&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
      Regular icons in light mode.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Note how again I only invert icons in &lt;code&gt;dark.css&lt;/code&gt; but not in &lt;code&gt;light.css&lt;/code&gt;, and how &lt;code&gt;:hover&lt;/code&gt;
gets a different inversion intensity in the two cases to make the icon appear
slightly darker or slightly brighter, dependent on the mode the user has selected.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* dark.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--icon-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;100%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token property&quot;&gt;--icon-filter_hover&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;40%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token selector&quot;&gt;;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;img[src*=&quot;.svg&quot;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--icon-filter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* light.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--icon-filter_hover&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;60%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* style.css */&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img[src*=&quot;.svg&quot;]:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--icon-filter_hover&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;use-currentcolor-for-inline-svgs&quot;&gt;Use &lt;code style=&quot;color: currentColor;&quot;&gt;currentColor&lt;/code&gt; for inline SVGs&lt;/h3&gt;
&lt;p&gt;For &lt;em&gt;inline&lt;/em&gt; SVG images, instead of &lt;a href=&quot;#invert-vector-graphics-and-icons&quot;&gt;using inversion filters&lt;/a&gt;,
you can leverage the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentColor_keyword&quot;&gt;&lt;code&gt;currentColor&lt;/code&gt;&lt;/a&gt;
CSS keyword that represents the value of an element&#39;s &lt;code&gt;color&lt;/code&gt; property.
This lets you use the &lt;code&gt;color&lt;/code&gt; value on properties that do not receive it by default.
Conveniently, if &lt;code&gt;currentColor&lt;/code&gt; is used as the value of the SVG
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Fills_and_Strokes#Fill_and_Stroke_Attributes&quot;&gt;&lt;code&gt;fill&lt;/code&gt; or &lt;code&gt;stroke&lt;/code&gt; attributes&lt;/a&gt;,
it instead takes its value from the inherited value of the color property.
Even better: this also works for
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use&quot;&gt;&lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;use href=&amp;quot;…&amp;quot;&amp;gt;&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/a&gt;,
so you can have separate resources
and &lt;code&gt;currentColor&lt;/code&gt; will still be applied in context.
Please note that this only works for &lt;em&gt;inline&lt;/em&gt; or &lt;code&gt;&amp;lt;use href=&amp;quot;…&amp;quot;&amp;gt;&lt;/code&gt; SVGs,
but not SVGs that are referenced as the &lt;code&gt;src&lt;/code&gt; of an image or somehow via CSS.
You can see this applied in the demo below.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Some inline SVG --&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token attr-name&quot;&gt;stroke&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;currentColor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  […]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;height: 950px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; vr; encrypted-media&quot; src=&quot;https://glitch.com/embed/#!/embed/dark-mode-currentcolor?path=light.css&amp;previewSize=100&quot; alt=&quot;dark-mode-currentcolor on Glitch&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;smooth-transitions-between-modes&quot;&gt;Smooth transitions between modes &lt;a class=&quot;w-headline-link&quot; href=&quot;#smooth-transitions-between-modes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Switching from dark mode to light mode or vice versa can be smoothed thanks to the fact
that both &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;background-color&lt;/code&gt; are
&lt;a href=&quot;https://www.quackit.com/css/css3/animations/animatable_properties/&quot;&gt;animatable CSS properties&lt;/a&gt;.
Creating the animation is as easy as declaring two &lt;code&gt;transition&lt;/code&gt;s for the two properties.
The example below illustrates the overall idea, you can experience it live in the
&lt;a href=&quot;https://dark-mode-baseline.glitch.me/&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;--duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;   &lt;span class=&quot;token property&quot;&gt;--timing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--background-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    color &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--duration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--timing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    background-color &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--duration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--timing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;art-direction-with-dark-mode&quot;&gt;Art direction with dark mode &lt;a class=&quot;w-headline-link&quot; href=&quot;#art-direction-with-dark-mode&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While for loading performance reasons in general I recommend to exclusively work with &lt;code&gt;prefers-color-scheme&lt;/code&gt;
in the &lt;code&gt;media&lt;/code&gt; attribute of &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; elements (rather than inline in stylesheets),
there are situations where you actually may want to work with &lt;code&gt;prefers-color-scheme&lt;/code&gt; directly inline in your HTML code.
Art direction is such a situation.
On the web, art direction deals with the overall visual appearance of a page and how it communicates visually,
stimulates moods, contrasts features, and psychologically appeals to a target audience.&lt;/p&gt;
&lt;p&gt;With dark mode, it&#39;s up to the judgment of the designer to decide what is the best image at a particular mode
and whether &lt;a href=&quot;#photographic-images&quot;&gt;re-colorization of images&lt;/a&gt; is maybe &lt;em&gt;not&lt;/em&gt; good enough.
If used with the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element, the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; of the image to be shown can be made dependent on the &lt;code&gt;media&lt;/code&gt; attribute.
In the example below, I show the Western hemisphere for dark mode, and the Eastern hemisphere for light mode
or when no preference is given, defaulting to the Eastern hemisphere in all other cases.
This is of course purely for illustrative purposes.
Toggle dark mode on your device to see the difference.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;western.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: dark)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;eastern.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: light), (prefers-color-scheme: no-preference)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;eastern.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;height: 600px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; vr; encrypted-media&quot; src=&quot;https://glitch.com/embed/#!/embed/dark-mode-picture?path=index.html&amp;previewSize=100&quot; alt=&quot;dark-mode-picture on Glitch&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;dark-mode-but-add-an-opt-out&quot;&gt;Dark mode, but add an opt-out &lt;a class=&quot;w-headline-link&quot; href=&quot;#dark-mode-but-add-an-opt-out&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned in the &lt;a href=&quot;#why-dark-mode&quot;&gt;why dark mode&lt;/a&gt; section above,
dark mode is an aesthetic choice for most users.
In consequence, some users may actually like to have their operating system UI
in dark, but still prefer to see their webpages the way they are used to seeing them.
A great pattern is to initially adhere to the signal the browser sends through
&lt;code&gt;prefers-color-scheme&lt;/code&gt;, but to then optionally allow users to override their system-level setting.&lt;/p&gt;
&lt;h4&gt;The &lt;code style=&quot;color: currentColor;&quot;&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt; custom element&lt;/h4&gt;
&lt;p&gt;You can of course create the code for this yourself, but you can also just use
a ready-made custom element (web component) that I have created right for this purpose.
It&#39;s called &lt;a href=&quot;https://github.com/GoogleChromeLabs/dark-mode-toggle&quot;&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt;
and it adds a toggle (dark mode: on/off) or
a theme switcher (theme: light/dark) to your page that you can fully customize.
The demo below shows the element in action
(oh, and I have also 🤫 silently snuck it in all of the
&lt;a href=&quot;https://dark-mode-baseline.glitch.me/&quot;&gt;other&lt;/a&gt;
&lt;a href=&quot;https://dark-mode-currentcolor.glitch.me/&quot;&gt;examples&lt;/a&gt;
&lt;a href=&quot;https://dark-mode-picture.glitch.me/&quot;&gt;above&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dark-mode-toggle&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token attr-name&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Theme Switcher&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token attr-name&quot;&gt;appearance&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;switch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token attr-name&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Dark&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token attr-name&quot;&gt;light&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Light&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token attr-name&quot;&gt;remember&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Remember this&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dark-mode-toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-columns&quot;&gt;
  &lt;figure class=&quot;w-figure&quot;&gt;
    &lt;img style=&quot;height: 76px;&quot; src=&quot;https://web.dev/prefers-color-scheme/dark-mode-toggle-light.png&quot; alt=&quot;&lt;dark-mode-toggle&gt; in light mode.&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
      &lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt; in light mode.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;w-figure&quot;&gt;
    &lt;img style=&quot;height: 76px;&quot; src=&quot;https://web.dev/prefers-color-scheme/dark-mode-toggle-dark.png&quot; alt=&quot;&lt;dark-mode-toggle&gt; in light mode.&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
      &lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt; in dark mode.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Try clicking or tapping the dark mode controls in the upper right corner in the demo below.
If you check the checkbox in the third and the fourth control, see how your mode selection
is remembered even when you reload the page.
This allows your visitors to keep their operating system in dark mode,
but enjoy your site in light mode or vice versa.&lt;/p&gt;
&lt;div class=&quot;w-screenshot&quot; style=&quot;height: 800px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; vr; encrypted-media&quot; src=&quot;https://googlechromelabs.github.io/dark-mode-toggle/demo/index.html&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Working with and supporting dark mode is fun and opens up new design avenues.
For some of your visitors it can be the difference between not being able to handle your site
and being a happy user.
There are some pitfalls and careful testing is definitely required,
but dark mode is definitely a great opportunity for you to show that you care about all of your users.
The best practices mentioned in this post and helpers like the
&lt;a href=&quot;https://github.com/GoogleChromeLabs/dark-mode-toggle&quot;&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt; custom element
should make you confident in your ability to create an amazing dark mode experience.
&lt;a href=&quot;https://twitter.com/tomayac&quot;&gt;Let me know on Twitter&lt;/a&gt; what you create and if this post was useful
or also suggestions for improving it.
Thanks for reading! 🌒&lt;/p&gt;
&lt;h2 id=&quot;related-links&quot;&gt;Related links &lt;a class=&quot;w-headline-link&quot; href=&quot;#related-links&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Resources for the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chromestatus.com/feature/5109758977638400&quot;&gt;Chrome Platform Status page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crbug.com/889087&quot;&gt;Chromium bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme&quot;&gt;Media Queries Level 5 spec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources for the &lt;code&gt;color-scheme&lt;/code&gt; meta tag and CSS property:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chromestatus.com/feature/5330651267989504&quot;&gt;Chrome Platform Status page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://crbug.com/925935&quot;&gt;Chromium bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://drafts.csswg.org/css-color-adjust-1/&quot;&gt;CSS Color Adjustment Module Level 1 spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/3299&quot;&gt;CSS WG GitHub Issue for the meta tag and the CSS property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/whatwg/html/issues/4504&quot;&gt;HTML WHATWG GitHub Issue for the meta tag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;General dark mode links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://material.io/design/color/dark-theme.html&quot;&gt;Material Design—Dark Theme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webkit.org/blog/8892/dark-mode-in-web-inspector/&quot;&gt;Dark Mode in Web Inspector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webkit.org/blog/8840/dark-mode-support-in-webkit/&quot;&gt;Dark Mode Support in WebKit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/&quot;&gt;Apple Human Interface Guidelines—Dark Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Background research articles for this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/dev-channel/what-does-dark-modes-supported-color-schemes-actually-do-69c2eacdfa1d&quot;&gt;What Does Dark Mode&#39;s &amp;quot;supported-color-schemes&amp;quot; Actually Do? 🤔&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d&quot;&gt;Let there be darkness! 🌚 Maybe…&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b&quot;&gt;Re-Colorization for Dark Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements &lt;a class=&quot;w-headline-link&quot; href=&quot;#acknowledgements&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;prefers-color-scheme&lt;/code&gt; media feature, the &lt;code&gt;color-scheme&lt;/code&gt; CSS property,
and the related meta tag are the implementation work of 👏 &lt;a href=&quot;https://twitter.com/runeli&quot;&gt;Rune Lillesveen&lt;/a&gt;.
Rune is also a co-editor of the &lt;a href=&quot;https://drafts.csswg.org/css-color-adjust-1/&quot;&gt;CSS Color Adjustment Module Level 1&lt;/a&gt; spec.
I would like to 🙏 thank &lt;a href=&quot;https://www.linkedin.com/in/lukasz-zbylut/&quot;&gt;Lukasz Zbylut&lt;/a&gt;,
&lt;a href=&quot;https://twitter.com/rowan_m&quot;&gt;Rowan Merewood&lt;/a&gt;,
&lt;a href=&quot;https://www.linkedin.com/in/chiragd/&quot;&gt;Chirag Desai&lt;/a&gt;,
and &lt;a href=&quot;https://twitter.com/rob_dodson&quot;&gt;Rob Dodson&lt;/a&gt;
for their thorough reviews of this article.
The &lt;a href=&quot;#loading-strategy&quot;&gt;loading strategy&lt;/a&gt; is the brainchild of &lt;a href=&quot;https://twitter.com/jaffathecake&quot;&gt;Jake Archibald&lt;/a&gt;.
&lt;a href=&quot;https://twitter.com/ecbos_&quot;&gt;Emilio Cobos Álvarez&lt;/a&gt; has pointed me to the correct &lt;code&gt;prefers-color-scheme&lt;/code&gt; detection method.
The tip with referenced SVGs and &lt;code&gt;currentColor&lt;/code&gt; came from
&lt;a href=&quot;https://twitter.com/xeenon&quot;&gt;Timothy Hatcher&lt;/a&gt;.
Finally, I am thankful to the many anonymous participants of the various user studies
that have helped shape the recommendations in this article.
Hero image by &lt;a href=&quot;https://unsplash.com/photos/kujXUuh1X0o&quot;&gt;Nathan Anderson&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Top tips for web performance</title>
    <link href="https://web.dev/use-srcset-to-automatically-choose-the-right-image/"/>
    <updated>2019-06-23T17:00:00-07:00</updated>
    <id>https://web.dev/use-srcset-to-automatically-choose-the-right-image/</id>
    <content type="html">&lt;div class=&quot;w-youtube&quot;&gt;
  &lt;iframe class=&quot;w-youtube__embed&quot; src=&quot;https://www.youtube.com/embed/SyVKRnusyqM&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;According to &lt;a href=&quot;https://httparchive.org/reports/state-of-images&quot;&gt;HTTP Archive&lt;/a&gt;, a
typical mobile web page weighs over 2.6 MB, and more than two thirds of that
weight is images. That&#39;s a great opportunity for optimization!&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/http-archive.svg&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    &lt;a href=&quot;https://mobile.httparchive.org/&quot;&gt;Average mobile page bytes by content type&lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;tldr&quot;&gt;tl;dr &lt;a class=&quot;w-headline-link&quot; href=&quot;#tldr&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t save images larger than their display size.&lt;/li&gt;
&lt;li&gt;Save multiple sizes for each image and use the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt;
attribute to enable the browser to choose the smallest.
The &lt;code&gt;w&lt;/code&gt; value tells the browser the width of each version:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg 500w,&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;             medium.jpg 1000w,&lt;/span&gt;&lt;br&gt;             large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;     &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;save-images-with-the-right-size&quot;&gt;Save images with the right size &lt;a class=&quot;w-headline-link&quot; href=&quot;#save-images-with-the-right-size&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can make your website faster and less data hungry by using images with
dimensions that match the display size. In other words, give images the right
width and height when you save them.&lt;/p&gt;
&lt;p&gt;Take a look at the images below.&lt;/p&gt;
&lt;p&gt;They appear nearly identical, but the file size of one is more than 10 times
larger than the other.&lt;/p&gt;
&lt;div class=&quot;w-columns&quot;&gt;
  &lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
    &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/kittens-1000.jpg&quot; alt=&quot;Little Puss and Lias: two ten week old tabby kittens.&quot; width=&quot;300&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;Saved width 1000 px, file size 184 KB&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
    &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/kittens-300.jpg&quot; alt=&quot;Little Puss and Lias: two ten week old tabby kittens.&quot; width=&quot;300&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;Saved width 300 px, file size 16 KB&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The first image is much larger in file size because it&#39;s saved with dimensions
much larger than the display size. Both images are displayed with a fixed
width of 300 pixels, so it makes sense to use an image saved at the same
size.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For fixed widths, use images saved with the same dimensions as the
display size.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;but...-what-if-display-size-varies&quot;&gt;But… what if display size varies? &lt;a class=&quot;w-headline-link&quot; href=&quot;#but...-what-if-display-size-varies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In a multi-device world, images aren&#39;t always displayed at a single fixed size.&lt;/p&gt;
&lt;p&gt;Image elements might have a percentage width, or be part of responsive layouts
where image display sizes change to fit the screen size.&lt;/p&gt;
&lt;p&gt;…and what about pixel-hungry devices like Retina displays?&lt;/p&gt;
&lt;h2 id=&quot;help-the-browser-choose-the-right-image-size&quot;&gt;Help the browser choose the right image size &lt;a class=&quot;w-headline-link&quot; href=&quot;#help-the-browser-choose-the-right-image-size&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Wouldn&#39;t it be great if you could make each image available at different sizes, then
let the browser choose the best size for the device and display size?
Unfortunately there&#39;s a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Catch-22_(logic)&quot;&gt;catch-22&lt;/a&gt; when it comes to
working out which image is best. The browser should use the smallest possible
image, but it can&#39;t know the width of an image without downloading it to check.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;srcset&lt;/code&gt; comes in handy. You save images at different sizes, then
tell the browser the width of each version:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg 500w, medium.jpg 1000w, large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;     &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;w&lt;/code&gt; values show the width of each image in pixels. For example,
&lt;code&gt;small.jpg 500w&lt;/code&gt; tells the browser that &lt;a href=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/small.jpg&quot;&gt;small.jpg&lt;/a&gt; is 500
pixels wide. This enables the browser to choose the smallest possible image,
depending on the screen type and the viewport size—without having to
download images to check their size.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
&lt;code&gt;srcset&lt;/code&gt; gives the browser information about the
saved width of each image file.&lt;/p&gt;
&lt;p&gt;It does &lt;em&gt;not&lt;/em&gt; specify the size to display the image—you still need CSS for that!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can see &lt;code&gt;srcset&lt;/code&gt; in action for the image below. If you&#39;re on a laptop or
desktop computer, change your browser window size and reopen this page.
Then use the Network panel of your browser tools to check which image was used.
(You&#39;ll need to do that in an Incognito or Private window, otherwise the
original image file will be cached.)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/small.jpg&quot; srcset=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/small.jpg 500w, https://web.dev/use-srcset-to-automatically-choose-the-right-image/medium.jpg 1000w, https://web.dev/use-srcset-to-automatically-choose-the-right-image/large.jpg 1500w&quot; alt=&quot;Lias and Little Puss: two ten week old grey tabby kittens&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-can-i-create-multiple-image-sizes&quot;&gt;How can I create multiple image sizes? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-can-i-create-multiple-image-sizes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll need to make multiple sizes available for every image you want to use
with &lt;code&gt;srcset&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For one-off images such as hero images you can manually save different sizes. If
you have lots of images, such as product photos, you&#39;ll need to automate.
For that there are two approaches.&lt;/p&gt;
&lt;h3 id=&quot;incorporate-image-processing-in-your-build-process&quot;&gt;Incorporate image processing in your build process &lt;a class=&quot;w-headline-link&quot; href=&quot;#incorporate-image-processing-in-your-build-process&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As part of your build process, you can add steps to create different sized
versions of your images. See &lt;a href=&quot;https://web.dev/use-imagemin-to-compress-images&quot;&gt;&amp;quot;Use Imagemin to compress images&amp;quot;&lt;/a&gt;
to learn more.&lt;/p&gt;
&lt;h3 id=&quot;use-an-image-service&quot;&gt;Use an image service &lt;a class=&quot;w-headline-link&quot; href=&quot;#use-an-image-service&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image creation and delivery can be automated using a commercial service like
&lt;a href=&quot;https://cloudinary.com/&quot;&gt;Cloudinary&lt;/a&gt;, or an open source equivalent such as
&lt;a href=&quot;https://github.com/thumbor/thumbor&quot;&gt;Thumbor&lt;/a&gt; that you install and run yourself.&lt;/p&gt;
&lt;p&gt;You upload your high resolution images, and the image service automatically
creates and delivers different image formats and sizes depending on the URL
parameters. For an example, open &lt;a href=&quot;https://res.cloudinary.com/webdotdev/f_auto/w_500/IMG_20190113_113201.jpg&quot;&gt;this sample image on Cloudinary&lt;/a&gt; and try changing the &lt;code&gt;w&lt;/code&gt; value or the file extension in the URL bar.&lt;/p&gt;
&lt;p&gt;Image services also have more advanced features such as the ability to automate
&amp;quot;smart cropping&amp;quot; for different image sizes and automatically deliver &lt;a href=&quot;https://developers.google.com/speed/webp/&quot;&gt;WebP&lt;/a&gt; images
to browsers that support the format, instead of JPEGs—without changing the file
extension.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;You can check the format delivered using your browser tools.&lt;/p&gt;
&lt;p&gt;For the image URL above, a WebP file is automatically
delivered to browsers that support WebP, without changing the &lt;code&gt;.jpg&lt;/code&gt; file
extension.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/use-srcset-to-automatically-choose-the-right-image/devtools-headers-for-cloudinary-image.png&quot; alt=&quot;Chrome
DevTools showing WebP content-type header for file served by Cloudinary&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-if-the-image-doesn&#39;t-look-right-at-different-sizes&quot;&gt;What if the image doesn&#39;t look right at different sizes? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-if-the-image-doesn&#39;t-look-right-at-different-sizes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In that case, you&#39;ll need to use the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element for &amp;quot;art direction&amp;quot;:
providing a different image or image crop at different sizes. To learn more
take a look at the &lt;a href=&quot;https://web.dev/codelab-art-direction&quot;&gt;&amp;quot;Art direction&amp;quot;&lt;/a&gt; codelab.&lt;/p&gt;
&lt;h2 id=&quot;what-about-pixel-density&quot;&gt;What about pixel density? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-about-pixel-density&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;High-end devices have smaller (more dense) physical pixels. For example, a
high-end phone might have two or three times as many pixels in each row of
pixels as a cheaper device.&lt;/p&gt;
&lt;p&gt;That can affect the size you need to save your images. We won&#39;t go into the gory
details here, but you can find out more from the
&lt;a href=&quot;https://web.dev/codelab-density-descriptors&quot;&gt;&amp;quot;Use density descriptors&amp;quot;&lt;/a&gt; codelab.&lt;/p&gt;
&lt;h2 id=&quot;what-about-the-display-size-of-the-image&quot;&gt;What about the display size of the image? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-about-the-display-size-of-the-image&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use &lt;code&gt;sizes&lt;/code&gt; to make &lt;code&gt;srcset&lt;/code&gt; work even better.&lt;/p&gt;
&lt;p&gt;Without it, the browser uses the full width of the viewport when choosing an
image from a &lt;code&gt;srcset&lt;/code&gt;. The &lt;code&gt;sizes&lt;/code&gt; attribute tells the browser the width that an
image element will be displayed, so the browser can choose the smallest possible
image file—before it makes any layout calculations.&lt;/p&gt;
&lt;p&gt;In the example below, &lt;code&gt;sizes=&amp;quot;50vw&amp;quot;&lt;/code&gt; tells the browser that this image will be
displayed at 50% of the viewport width.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg 500w, medium.jpg 1000w, large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;     &lt;span class=&quot;token attr-name&quot;&gt;sizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;50vw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;     &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see this in action at
&lt;a href=&quot;https://simpl.info/sizeswvalues/&quot;&gt;simpl.info/sizes&lt;/a&gt; and the &lt;a href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths&quot;&gt;&amp;quot;Specifying multiple slot widths&amp;quot;&lt;/a&gt; codelab.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
&lt;code&gt;sizes&lt;/code&gt; gives the browser information about the display width
of on image element.&lt;/p&gt;
&lt;p&gt;As with &lt;code&gt;srcset&lt;/code&gt; it does NOT specify the size to display the image—you need
CSS for that.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;what-about-browser-support&quot;&gt;What about browser support? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-about-browser-support&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; are &lt;a href=&quot;https://caniuse.com/#feat=srcset&quot;&gt;supported by over 90% of
browsers globally&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If a browser does not support &lt;code&gt;srcset&lt;/code&gt; or &lt;code&gt;sizes&lt;/code&gt; it will fall back to just using the &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;This makes &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; great progressive enhancements!&lt;/p&gt;
&lt;h2 id=&quot;learn-more&quot;&gt;Learn more &lt;a class=&quot;w-headline-link&quot; href=&quot;#learn-more&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take a look at the &lt;a href=&quot;https://web.dev/fast#optimize-your-images&quot;&gt;&amp;quot;Optimize your images&amp;quot;&lt;/a&gt; section
of web.dev for a deeper dive into image optimization. For a more guided
experience, consider trying the free &lt;a href=&quot;https://udacity.com/course/responsive-images--ud882&quot;&gt;&amp;quot;Responsive
Images&amp;quot;&lt;/a&gt; course offered by
Udacity.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;This post accompanies &lt;a href=&quot;https://www.youtube.com/playlist?list=PLNYkxOF6rcICVl6Vb-AFlw81bQLuv6a_P&quot;&gt;&lt;strong&gt;Top tips for web performance&lt;/strong&gt;&lt;/a&gt;: a fortnightly video series showing simple techniques to improve site speed.&lt;/p&gt;
&lt;/div&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Bringing service workers to Google Search</title>
    <link href="https://web.dev/google-search-sw/"/>
    <updated>2019-06-19T17:00:00-07:00</updated>
    <id>https://web.dev/google-search-sw/</id>
    <content type="html">&lt;h2 id=&quot;background&quot;&gt;Background &lt;a class=&quot;w-headline-link&quot; href=&quot;#background&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Search for just about any topic on Google, and you&#39;re presented with an
instantly recognizable page of meaningful, relevant results. What you probably
&lt;em&gt;didn&#39;t&lt;/em&gt; realize is that this search results page is, under certain scenarios,
served by a powerful piece of web technology called a
&lt;a href=&quot;https://web.dev/service-workers-cache-storage/&quot;&gt;service worker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Rolling out service worker support for Google Search without negatively
impacting the performance required dozens of engineers working across multiple
teams. This is the story of what shipped, how performance was measured, and what
tradeoffs were made.&lt;/p&gt;
&lt;h2 id=&quot;key-reasons-for-exploring-service-workers&quot;&gt;Key reasons for exploring service workers &lt;a class=&quot;w-headline-link&quot; href=&quot;#key-reasons-for-exploring-service-workers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Adding a service worker to a web app, just like making any architectural change
to your site, should be done with a clear set of goals in mind. For the Google
Search team, there were a few key reasons why adding a service worker was worth
exploring.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;A service worker is extra code that sits in between your web app and
the network, and running that code isn&#39;t free, so you need to make sure that
what you&#39;re doing inside the service worker adds enough of a caching or
functionality benefit to justify the cost of running the code. (This &lt;a href=&quot;https://www.youtube.com/watch?v=25aCD5XL1Jk&quot;&gt;talk&lt;/a&gt;
at the Chrome Dev Summit 2018 does a great job of exploring that idea in more
detail.) An upfront understanding what you hope to achieve—and then collecting a
full set of metrics to ensure that you&#39;ve actually achieved it—should be the
first step in your service worker journey.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;limited-search-result-caching&quot;&gt;Limited search result caching &lt;a class=&quot;w-headline-link&quot; href=&quot;#limited-search-result-caching&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Google Search team found that it&#39;s common for users to search for the
same terms more than once within a short period of time. Rather than trigger a
new backend request just to get what&#39;s likely to be the same results, the Search
team wanted to take advantage of caching and fulfill those repeat requests
locally.&lt;/p&gt;
&lt;p&gt;The importance of freshness can&#39;t be discounted, and sometimes users search for
the same terms repeatedly because it&#39;s an evolving topic, and they expect to see
fresh results. Using a service worker allows the Search team to implement
fine-grained logic to control the lifetime of locally cached search results, and
achieve the exact balance of speed vs. freshness that they believe best serves
users.&lt;/p&gt;
&lt;h3 id=&quot;meaningful-offline-experience&quot;&gt;Meaningful offline experience &lt;a class=&quot;w-headline-link&quot; href=&quot;#meaningful-offline-experience&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Additionally, the Google Search team wanted to provide a meaningful offline
experience. When a user wants to find out about a topic, they want to go
straight to the Google Search page and start searching, without worrying about
an active Internet connection.&lt;/p&gt;
&lt;p&gt;Without a service worker, visiting the Google search page while offline would
just lead to the browser&#39;s standard network error page, and users would have to
remember to come back and try again once their connection returned. With a
service worker, it&#39;s possible to serve a custom offline HTML response, and allow
users to enter their search query immediately.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/google-search-sw/offline-screenshot.png&quot; alt=&quot;A screenshot of the background retry interface.&quot;&gt;&lt;/p&gt;
&lt;p&gt;The results won&#39;t be available until there&#39;s an Internet connection, but the
service worker allows the search to be deferred and sent to Google&#39;s servers as
soon as the device goes back online using the
&lt;a href=&quot;https://developers.google.com/web/updates/2015/12/background-sync&quot;&gt;background sync API&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;smarter-javascript-caching-and-serving&quot;&gt;Smarter JavaScript caching and serving &lt;a class=&quot;w-headline-link&quot; href=&quot;#smarter-javascript-caching-and-serving&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another motivation was to optimize the caching and loading of the modularized
JavaScript code that powers the various types of features on the search results
page. There are a number of benefits offered by JavaScript bundling that make
sense when there&#39;s no service worker involvement, so the Search team did not
want to simply stop bundling entirely.&lt;/p&gt;
&lt;p&gt;By using a service worker&#39;s ability to version and cache fine-grained chunks of
JavaScript at runtime, the Search team suspected that they could reduce the
amount of cache churn and ensure that JavaScript reused in the
future can be cached efficiently. The logic inside of their service worker can
analyze an outgoing HTTP request for a bundle that contains multiple JavaScript
modules, and fulfill it by piecing together multiple, locally cached
modules—effectively &amp;quot;unbundling&amp;quot; when possible. This saves user bandwidth, and
improves overall responsiveness.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--success&quot;&gt;
&lt;p&gt;&lt;strong&gt;Success:&lt;/strong&gt;
On average, repeat visits handled by the service worker
result in &lt;strong&gt;half as much new JavaScript downloaded&lt;/strong&gt;, and that directly leads to
&lt;strong&gt;6% fewer delayed user interactions&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;There are also performance benefits of using cached JavaScript served by a
service worker: in Chrome, &lt;a href=&quot;https://v8.dev/blog/code-caching-for-devs#use-service-worker-caches&quot;&gt;a parsed, byte code representation&lt;/a&gt;
of that JavaScript is stored and reused, leading to less work that needs to be
done at runtime in order to execute the JavaScript on the page.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-solutions&quot;&gt;Challenges and solutions &lt;a class=&quot;w-headline-link&quot; href=&quot;#challenges-and-solutions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here are a few of the hurdles that needed to be overcome in order to achieve the
team&#39;s stated goals. While some of these challenges are specific to Google
Search, many of them are applicable to a wide range of sites that might be
considering a service worker deployment.&lt;/p&gt;
&lt;h3 id=&quot;problem:-service-worker-overhead&quot;&gt;Problem: service worker overhead &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem:-service-worker-overhead&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The biggest challenge, and the one true blocker for launching a service worker
on Google Search, was to ensure that it did not do anything that might increase
user-perceived latency. Google Search takes performance &lt;em&gt;very&lt;/em&gt; seriously, and in
the past, has blocked launches of new functionality if it contributed even tens
of milliseconds of additional latency for a given user population.&lt;/p&gt;
&lt;p&gt;When the team started collecting performance data during their earliest
experiments, it became obvious that there would be a problem. The HTML returned
in response to
&lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests&quot;&gt;navigation requests&lt;/a&gt;
for the search result page is dynamic, and varies greatly depending on logic
that needs to run on Search&#39;s web servers. There&#39;s currently no way for the
service worker to replicate this logic and return cached HTML immediately—the
best it could do is to pass along navigation requests to the backend web
servers, which necessitates a network request.&lt;/p&gt;
&lt;p&gt;Without a service worker, this network request happens immediately upon user
navigation. When a service worker is registered, it always needs to be started
up and given a chance to execute its
&lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/#cache_and_return_requests&quot;&gt;&lt;code&gt;fetch&lt;/code&gt; event handlers&lt;/a&gt;,
even when there&#39;s no chance those fetch handlers will do anything other than go
to the network. The amount of time that it takes to start up and run the service
worker code is pure overhead added on top of every navigation:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/google-search-sw/no-preload.png&quot; alt=&quot;An illustration of the SW startup blocking the navigation request.&quot;&gt;&lt;/p&gt;
&lt;p&gt;This puts the service worker implementation at too much of a latency
disadvantage to justify any other benefits. Additionally, the team found that,
based on measuring service worker boot times on real-world devices, there was a
wide distribution of startup times, with some low-end mobile devices taking
almost as much time to start up the service worker as it might take to make the
network request for the results page&#39;s HTML.&lt;/p&gt;
&lt;h3 id=&quot;solution:-use-navigation-preload&quot;&gt;Solution: use navigation preload &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution:-use-navigation-preload&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The single, most crucial feature that allowed the Google Search team to move
ahead with their service worker launch is
&lt;a href=&quot;https://developers.google.com/web/updates/2017/02/navigation-preload&quot;&gt;navigation preload&lt;/a&gt;.
Using navigation preload is a key performance win for any service worker that
needs to use a response from the network to satisfy navigation requests. It
provides a hint to the browser to start making the navigation request
immediately, at the same time as the service worker starts up:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/google-search-sw/with-preload.png&quot; alt=&quot;An illustration of the SW startup done in parallel with the navigation request.&quot;&gt;&lt;/p&gt;
&lt;p&gt;As long as the amount of time it takes for the service worker to start up is
less than the amount of time it takes to get a response back from the network,
there shouldn&#39;t be any latency overhead introduced by the service worker.&lt;/p&gt;
&lt;p&gt;The Search team also needed to avoid using a service worker on low-end mobile
devices where the service worker boot time could exceed the navigation request.
Since there&#39;s no hard-and-fast rule for what constitutes a &amp;quot;low-end&amp;quot; device,
they came up with the heuristic of
&lt;a href=&quot;https://developers.google.com/web/updates/2017/12/device-memory&quot;&gt;checking the total RAM&lt;/a&gt;
installed on the device. Anything less than 2 gigabytes of memory fell into
their low-end device category, where service worker startup time would be unacceptable.&lt;/p&gt;
&lt;p&gt;Available storage space is another consideration, since the full set of
resources to be cached for future use can run to several megabytes. The
&lt;a href=&quot;https://developers.google.com/web/updates/2017/08/estimating-available-storage-space&quot;&gt;&lt;code&gt;navigator.storage&lt;/code&gt; interface&lt;/a&gt;
allows the Google Search page to figure out in advance whether their attempts to
cache data run the risk of failing due to storage quota failures.&lt;/p&gt;
&lt;p&gt;This left the Search team with multiple pieces of criteria that they could use
to determine whether or not to use a service worker: if a user comes to the
Google Search page using a browser that supports navigation preload, and has at
least 2 gigabytes of RAM, and enough free storage space, then a
&lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#the_first_service_worker&quot;&gt;service worker is registered&lt;/a&gt;.
Browsers or devices that don&#39;t meet that criteria won&#39;t end up with a service
worker, but they&#39;ll still see the same Google Search experience as they always
have.&lt;/p&gt;
&lt;p&gt;One side benefit of this selective registration is the ability to ship a
smaller, more efficient service worker. Targeting fairly modern browsers to run
the service worker code eliminates the overhead of transpilation and polyfills
for older browsers. This ended up cutting out around 8 kilobytes of uncompressed
JavaScript code from the total size of the service worker&#39;s implementation.&lt;/p&gt;
&lt;h3 id=&quot;problem:-service-worker-scopes&quot;&gt;Problem: service worker scopes &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem:-service-worker-scopes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once the Search team ran enough latency experiments and were confident that
using navigation preload offered them a viable, latency-neutral path for using a
service worker, some practical issues started moving to the forefront. One of
those issues has to do with service worker&#39;s
&lt;a href=&quot;https://developers.google.com/web/ilt/pwa/introduction-to-service-worker#registration_and_scope&quot;&gt;scoping rules&lt;/a&gt;.
A service worker&#39;s scope determines which pages it can potentially take control
of.&lt;/p&gt;
&lt;p&gt;Scoping works based on the URL path prefix. For domains that host a single
web app, this isn&#39;t an issue, as you&#39;d normally just use a service worker with
the maximal scope of &lt;code&gt;/&lt;/code&gt;, which could take control of any page under the domain.
But Google Search&#39;s URL structure is a little more complicated.&lt;/p&gt;
&lt;p&gt;If the service worker were given the maximal scope of &lt;code&gt;/&lt;/code&gt;, it would end up being
able to take control of any page hosted under &lt;code&gt;www.google.com&lt;/code&gt; (or the regional
equivalent), and there are URLs under that domain that have nothing to do with
Google Search. A more reasonable, restrictive scope would be &lt;code&gt;/search&lt;/code&gt;, which at
least would eliminate URLs completely unrelated to search results.&lt;/p&gt;
&lt;p&gt;Unfortunately, even that &lt;code&gt;/search&lt;/code&gt; URL path is shared amongst different flavor
of Google Search results, with URL query parameters determining which specific
type of search result is shown. Some of those flavors use completely different
codebases than the traditional web search result page. For example, Image Search
and Shopping Search are both served under the &lt;code&gt;/search&lt;/code&gt; URL path with different
query parameters, but neither of those interfaces were ready to ship their own
service worker experience (yet).&lt;/p&gt;
&lt;h3 id=&quot;solution:-create-a-dispatch-and-routing-framework&quot;&gt;Solution: create a dispatch and routing framework &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution:-create-a-dispatch-and-routing-framework&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While there are &lt;a href=&quot;https://github.com/w3c/ServiceWorker/issues/1373&quot;&gt;some proposals&lt;/a&gt;
that allow for something more powerful than URL path prefixes to determine
service worker scopes, the Google Search team was stuck deploying a service
worker that did nothing for a subset of pages it controlled.&lt;/p&gt;
&lt;p&gt;To work around this, the Google Search team built up a bespoke dispatch and
routing framework that could be configured to check for criteria like the query
parameters of the client page, and use those to determine which specific code
path to go down. Rather than hardcoding rules, the system was built to be
flexible and allow teams that share the URL space, like Image Search and
Shopping Search, to drop in their own service worker logic down the line, if
they decide to implement it.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;While this custom solution is internal to Google, the same general principle can
be applied to any domain that includes a number of different logical web apps,
all of whom live under a common URL.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;problem:-personalized-results-and-metrics&quot;&gt;Problem: personalized results and metrics &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem:-personalized-results-and-metrics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Users can sign in to Google Search using their Google Accounts, and their search
results experience may be customized based on their particular account data.
Logged in users are identified by specific &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies&quot;&gt;browser cookies&lt;/a&gt;,
which is a venerable and widely-supported standard.&lt;/p&gt;
&lt;p&gt;One downside of using browser cookies, though, is that they are not exposed
inside of a service worker, and there is no way of automatically examining their
values and ensuring that they have not changed due to a user logging out or
switching accounts. (There is effort underway to
&lt;a href=&quot;https://developers.google.com/web/updates/2018/09/asynchronous-access-to-http-cookies#welcome_service_workers&quot;&gt;bring cookie access to service workers&lt;/a&gt;,
but as of this writing, the approach is
&lt;a href=&quot;https://developers.google.com/web/updates/2018/09/asynchronous-access-to-http-cookies#origin-trial&quot;&gt;experimental&lt;/a&gt;
and is not widely supported.)&lt;/p&gt;
&lt;p&gt;A mismatch between the service worker&#39;s view of the current logged in user and
the actual user logged in to the Google Search web interface could lead to
incorrectly personalized search results or misattributed metrics and logging.
Any of those failure scenarios would be a serious issue for the Google Search
team.&lt;/p&gt;
&lt;h3 id=&quot;solution:-send-cookies-using-postmessage&quot;&gt;Solution: send cookies using postMessage &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution:-send-cookies-using-postmessage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rather than wait for experimental APIs to launch and provide direct access to
the browser&#39;s cookies inside of a service worker, the Google Search team went
with a stop-gap solution: whenever a page controlled by the service worker is
loaded, the page reads the relevant cookies and uses
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage&quot;&gt;&lt;code&gt;postMessage()&lt;/code&gt;&lt;/a&gt;
to send them to the service worker.&lt;/p&gt;
&lt;p&gt;The service worker then checks the current cookie value against the value
that it expects, and if there&#39;s a mismatch, it takes steps to purge any
user-specific data from its storage, and reloads the search results page without
any incorrect personalization.&lt;/p&gt;
&lt;p&gt;The specific steps that the service worker takes to reset things to a baseline
are particular to Google Search&#39;s requirements, but the same general approach
may be useful to other developers who deal with personalized data keyed off of
browsers cookies.&lt;/p&gt;
&lt;h3 id=&quot;problem:-experiments-and-dynamism&quot;&gt;Problem: experiments and dynamism &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem:-experiments-and-dynamism&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned, the Google Search team relies heavily on running experiments in
production, and testing the effects of new code and features in the real world
before turning them on by default. This can be a bit of a challenge with a
static service worker that relies heavily on cached data, since opting users in
and out of experiments often requires communication with the backend server.&lt;/p&gt;
&lt;h3 id=&quot;solution:-dynamically-generated-service-worker-script&quot;&gt;Solution: dynamically generated service worker script &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution:-dynamically-generated-service-worker-script&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The solution that the team went with was to use a dynamically generated service
worker script, customized by the web server for each individual user, instead of
a single, static service worker script that gets generated ahead of time.
Information about experiments that might affect the service worker&#39;s behavior or
network requests in general are included directly in this customized service
worker scripts. Changing the sets of active experiences for a user is done via a
combination of traditional techniques, like browser cookies, as well as serving
updated code in the registered service worker URL.&lt;/p&gt;
&lt;p&gt;Using a dynamically generated service worker script also makes it easier to
provide an escape hatch in the unlikely event that a service worker
implementation has a fatal bug that needs to be avoided. The dynamic server
worker response could be a &lt;a href=&quot;https://stackoverflow.com/a/38980776/385997&quot;&gt;no-op implementation&lt;/a&gt;,
effectively disabling the service worker for some or all of the current users.&lt;/p&gt;
&lt;h3 id=&quot;problem:-coordinating-updates&quot;&gt;Problem: coordinating updates &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem:-coordinating-updates&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the toughest challenges facing any real-world service worker deployment
is to devise a reasonable tradeoff between avoiding the network in favor of the
cache, while at the same time, ensuring that existing users get critical updates
and changes soon after they&#39;re deployed to production. The right balance depends
on a lot of factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Whether your web app is a long-lived &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot;&gt;single page app&lt;/a&gt;
that a user keeps open indefinitely, without navigating to new pages.&lt;/li&gt;
&lt;li&gt;What the deployment cadence is for updates to your backend web server.&lt;/li&gt;
&lt;li&gt;Whether the average user would tolerate using a slightly out-of-date version
of your web app, or whether freshness is the top priority.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While experimenting with service workers, the Google Search team made sure to
keep the experiments running across a number of scheduled backend updates, to
ensure that the metrics and user experience would more closely match what return
users would end up seeing in the real-world.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
It&#39;s important to remember that shipping a service worker
is &lt;strong&gt;not a one-time deployment&lt;/strong&gt;—you need to have a process in place, tailored to
your own production infrastructure, to make sure that updates happen smoothly
over time!&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;solution:-balance-freshness-and-cache-utilization&quot;&gt;Solution: balance freshness and cache-utilization &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution:-balance-freshness-and-cache-utilization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After testing a number of different configuration options, the Google Search
team found that the following setup provided the right balance between freshness
and cache-utilization.&lt;/p&gt;
&lt;p&gt;The service worker script URL is served with the
&lt;code&gt;Cache-Control: private, max-age=1500&lt;/code&gt; (1500 seconds, or 25 minutes) response
header, and is
&lt;a href=&quot;https://developers.google.com/web/updates/2018/06/fresher-sw#updateviacache&quot;&gt;registered with updateViaCache set to &#39;all&#39;&lt;/a&gt;
to ensure that the header is honored. The Google Search web backend is, as you
might imagine, a large, globally distributed set of servers that requires as
close to 100% uptime as possible. Deploying a change that would affect the
service worker script&#39;s contents is done in a rolling fashion.&lt;/p&gt;
&lt;p&gt;If a user hits a backend that has been updated, and then quickly navigates to
another page which hits a backend that hasn&#39;t yet received the updated service
worker, they&#39;d end up flip-flopping between versions multiple times. Therefore,
telling the browser to only bother checking for an updated script if 25 minutes
has passed since the last check does not have a significant downside. The upside
of opting-in to this behavior is cutting down significantly on the traffic
received by the endpoint that dynamically generates the service worker script.&lt;/p&gt;
&lt;p&gt;Additionally, an ETag header is set on the service worker script&#39;s HTTP
response, ensuring that when an update check is made after 25 minutes has
passed, the server can respond efficiently with an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304&quot;&gt;HTTP 304&lt;/a&gt;
response if there haven&#39;t been any updates to the service worker deployed in the
interim.&lt;/p&gt;
&lt;p&gt;While some interactions within the Google Search web app use single page
app-style navigations (i.e. via the
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries&quot;&gt;History API&lt;/a&gt;),
for the most part, Google Search is a traditional web app that uses &amp;quot;real&amp;quot;
navigations. This comes into play when the team decided that it would be
effective to use two options that accelerate the service worker update
lifecycle:
&lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim&quot;&gt;&lt;code&gt;clients.claim()&lt;/code&gt;&lt;/a&gt;
and
&lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase&quot;&gt;&lt;code&gt;skipWaiting()&lt;/code&gt;&lt;/a&gt;.
Clicking around Google Search&#39;s interface generally ends up navigating to new
HTML documents. Calling &lt;code&gt;skipWaiting&lt;/code&gt; ensures that an updated service worker
gets a chance to handle those new navigation requests immediately after
installation. Similarly, calling &lt;code&gt;clients.claim()&lt;/code&gt; means that the updated
service worker gets a chance to start controlling any open Google Search pages
that are uncontrolled, following service worker activation.&lt;/p&gt;
&lt;p&gt;The approach that Google Search went with isn&#39;t necessarily a solution that
works for everyone—it was the result of carefully A/B testing various
combinations of serving options until they found what worked best for them.
Developers whose backend infrastructure allow them to deploy updates more
quickly might prefer that the browser check for an updated service worker script
as frequently as possible, by
&lt;a href=&quot;https://developers.google.com/web/updates/2018/06/fresher-sw#whats_changing&quot;&gt;always ignoring the HTTP cache&lt;/a&gt;.
If you&#39;re building a single page app that users will might keep open for a long
period of time, using &lt;code&gt;skipWaiting()&lt;/code&gt; is probably not the right choice for
you—you
&lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase&quot;&gt;risk running into cache inconsistencies&lt;/a&gt;
if you allow the new service worker to activate while there are long-lived
clients.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key Takeaways &lt;a class=&quot;w-headline-link&quot; href=&quot;#key-takeaways&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;by-default-service-workers-aren&#39;t-performance-neutral&quot;&gt;By default, service workers aren&#39;t performance neutral &lt;a class=&quot;w-headline-link&quot; href=&quot;#by-default-service-workers-aren&#39;t-performance-neutral&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Adding a service worker to your web app means inserting an additional piece of
JavaScript that needs to be loaded and executed before your web app gets
responses to its requests. If those responses end up coming from a local cache
rather than from the network, then the overhead of running the service worker
is usually negligible in comparison to the performance win from going
cache-first. But if you know that your service worker always has to
consult the network when handling navigation requests, using navigation preload
is a crucial performance win.&lt;/p&gt;
&lt;h3 id=&quot;service-workers-are-(still!)-a-progressive-enhancement&quot;&gt;Service workers are (still!) a progressive enhancement &lt;a class=&quot;w-headline-link&quot; href=&quot;#service-workers-are-(still!)-a-progressive-enhancement&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The service worker support story is much brighter today than it was even a year
ago. All modern browsers now feature at least some
&lt;a href=&quot;https://jakearchibald.github.io/isserviceworkerready/&quot;&gt;support for service workers&lt;/a&gt;,
but unfortunately, there are some advanced service worker features—like
background sync and navigation preload—that aren&#39;t rolled out universally.
Feature checking for the specific subset of features that you know you need, and
only registering a service worker when those are present, is still a reasonable
approach to take.&lt;/p&gt;
&lt;p&gt;Similarly, if you&#39;ve run experiments in the wild, and know that low-end devices
end up performing poorly with the additional overhead of a service worker, you
can abstain from registering a service worker in those scenarios as well.&lt;/p&gt;
&lt;p&gt;You should continue to treat service workers as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Progressive_enhancement&quot;&gt;progressive enhancement&lt;/a&gt;
that gets added to a web app when all the prerequisites are met and the service
worker adds something positive to user experience and overall loading
performance.&lt;/p&gt;
&lt;h3 id=&quot;measure-everything&quot;&gt;Measure everything &lt;a class=&quot;w-headline-link&quot; href=&quot;#measure-everything&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The only way you can figure out whether shipping a service worker has had a
positive or negative impact on your users&#39; experiences is to experiment and
measure the results.&lt;/p&gt;
&lt;p&gt;The specifics of setting up meaningful measurements depends on what
analytics provider you&#39;re using, and how you normally conduct experiments in
your deployment setup. One approach, using Google Analytics to collect metrics,
is detailed in
&lt;a href=&quot;https://developers.google.com/web/showcase/2016/service-worker-perf&quot;&gt;this case study&lt;/a&gt;
based on the experience using service workers in the Google I/O web app.&lt;/p&gt;
&lt;h2 id=&quot;non-goals&quot;&gt;Non-goals &lt;a class=&quot;w-headline-link&quot; href=&quot;#non-goals&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While many in the web development community associate service workers with &lt;a href=&quot;https://developers.google.com/web/progressive-web-apps/&quot;&gt;Progressive Web Apps&lt;/a&gt;,
building a &amp;quot;Google Search PWA&amp;quot; was not an initial goal of the team. The Google
Search web app doesn&#39;t currently provide metadata via a
&lt;a href=&quot;https://developers.google.com/web/fundamentals/web-app-manifest/&quot;&gt;web app manifest&lt;/a&gt;,
nor does it encourage users to go through the
&lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/&quot;&gt;Add to Homescreen flow&lt;/a&gt;.
The Search team is currently satisfied with users coming to their web app via
the traditional entry points for Google Search.&lt;/p&gt;
&lt;p&gt;Rather than trying to turn the Google Search web experience into the equivalent
of what you&#39;d expect from an installed application, the focus on the initial
roll out was to progressively enhance the existing web site.&lt;/p&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements &lt;a class=&quot;w-headline-link&quot; href=&quot;#acknowledgements&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thanks to the entire Google Search web development team for their work on the
service worker implementation, and for sharing the background material that went
into writing this article. Particular thanks goes to Philippe Golle, Rajesh
Jagannathan, R. Samuel Klatchko, Andy Martone, Leonardo Peña, Rachel Shearer,
Greg Terrono, and Clay Woolam.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How we&#39;re bringing Google Earth to the web</title>
    <link href="https://web.dev/earth-webassembly/"/>
    <updated>2019-06-19T17:00:00-07:00</updated>
    <id>https://web.dev/earth-webassembly/</id>
    <content type="html">&lt;p&gt;In an ideal world, every application that developers build, regardless of technology, would be available in the browser. But there are barriers to bringing projects to the web, depending on the technology they were built with and how well that technology is supported by the various browser vendors. &lt;a href=&quot;https://webassembly.org/&quot;&gt;WebAssembly&lt;/a&gt; (Wasm) is a compile target standardized by the &lt;a href=&quot;https://www.w3.org/&quot;&gt;W3C&lt;/a&gt; that helps us solve this problem by allowing us to run codebases from languages other than JavaScript on the web.&lt;/p&gt;
&lt;p&gt;We&#39;ve done just that with Google Earth, available today in &lt;a href=&quot;https://g.co/earth/beta&quot;&gt;preview beta&lt;/a&gt; on WebAssembly. Keep in mind that this is still a beta of Google Earth and may not be as smooth as you&#39;re used to (try out regular &lt;a href=&quot;https://earth.google.com/web/&quot;&gt;Earth for web&lt;/a&gt;). You can experiment with this beta in Chrome and other Chromium-based browsers, including Edge (Canary version) and Opera, as well as Firefox. Consider this beta your inspiration if you too are looking for better cross-browser support for your native applications.&lt;/p&gt;
&lt;h2 id=&quot;why-we-chose-webassembly-for-google-earth&quot;&gt;Why we chose WebAssembly for Google Earth &lt;a class=&quot;w-headline-link&quot; href=&quot;#why-we-chose-webassembly-for-google-earth&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We originally wrote most of Google Earth in C++ because it was a native application intended for desktop install. Then we were able to port it to Android and iOS as smartphones took hold, retaining most of our C++ codebase using &lt;a href=&quot;https://developer.android.com/ndk&quot;&gt;NDK&lt;/a&gt; and &lt;a href=&quot;https://www.wikipedia.org/wiki/Objective-C#Objective-C++&quot;&gt;Objective-C++&lt;/a&gt;. In 2017, when we brought Earth to the web, we used &lt;a href=&quot;https://developer.chrome.com/native-client&quot;&gt;Native Client&lt;/a&gt; (NaCl) to compile the C++ code and run it in the Chrome browser.&lt;/p&gt;
&lt;p&gt;At the time, NaCl was the only browser technology that allowed us to port our C++ code to the browser and give us the kind of performance Earth needed. Unfortunately, NaCl was a Chrome-only technology that never saw adoption across browsers. Now we&#39;re starting to switch to WebAssembly, which lets us take that same code and run it across browsers. This means Earth will be available to more people across the web.&lt;/p&gt;
 &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/earth-webassembly/earth-wasm-big.webp&quot; alt=&quot;A screenshot of Earth showing Eiffel Tower&quot;&gt;
&lt;h2 id=&quot;a-thread-on-threading&quot;&gt;A thread on threading &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-thread-on-threading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebAssembly is still evolving as a standard, and browsers continue to get extended with more features and functionality. From the Earth perspective, the most significant difference in support for WebAssembly between browsers is support for threading. Some browsers offer multi-threading support and others don&#39;t. Think of Earth like a huge 3D video game of the real world. As such, we&#39;re constantly streaming data to the browser, decompressing it and making it ready for rendering to the screen. Being able to do this work on a background thread has shown a clear improvement in &lt;a href=&quot;https://medium.com/google-earth/performance-of-web-assembly-a-thread-on-threading-54f62fd50cf7&quot;&gt;the performance of Earth in the browser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Multi-threaded WebAssembly relies on a browser feature called SharedArrayBuffer, which was pulled from browsers after the Spectre and Meltdown security vulnerabilities were revealed. To mitigate potential damage from attacks, Chrome&#39;s security team &lt;a href=&quot;https://security.googleblog.com/2018/07/mitigating-spectre-with-site-isolation.html&quot;&gt;introduced Site Isolation&lt;/a&gt; in Chrome for all desktop operating systems. Site Isolation limits each renderer process to documents from a single site. With this security feature in place, Chrome re-enabled SharedArrayBuffer for desktop—which allowed us to use multi-threaded WebAssembly with Earth for Chrome.&lt;/p&gt;
&lt;p&gt;Other browsers are working on Site Isolation or other mitigations in order to re-enable SharedArrayBuffer. In the meantime, Earth runs single-threaded in those browsers.&lt;/p&gt;
&lt;h2 id=&quot;how-webassembly-works-with-different-browsers&quot;&gt;How WebAssembly works with different browsers &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-webassembly-works-with-different-browsers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&#39;ve learned a lot about the state of WebAssembly support in browsers porting Earth. If you&#39;re going to develop applications using WebAssembly, it&#39;s important to understand the current state of how WebAssembly works with different browsers.&lt;/p&gt;
&lt;h3 id=&quot;edge&quot;&gt;Edge &lt;a class=&quot;w-headline-link&quot; href=&quot;#edge&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Edge is on the verge of becoming two distinct development experiences based on Microsoft&#39;s choice to move from the EdgeHTML renderer over to a Chromium-based renderer. At the moment, the Google Earth beta on WebAssembly won&#39;t run on the current public version of Edge due to lack of support for WebGL2. That will be fixed once the new version of Edge, based on Chromium, ships in the near future. In the meantime, you can &lt;a href=&quot;https://www.microsoftedgeinsider.com/download&quot;&gt;download the Canary version of Edge&lt;/a&gt; and see that Earth works quite well.&lt;/p&gt;
&lt;h3 id=&quot;chrome&quot;&gt;Chrome &lt;a class=&quot;w-headline-link&quot; href=&quot;#chrome&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chrome has strong support for WebAssembly, including multi-threading on desktop, so you can expect Earth to run smoother as a result. However, we look forward to Chrome adding support for dynamic memory allocation with multi-threading in WebAssembly. Until then, Earth may fail to start on devices with limited amounts of memory (such as 32-bit machines).&lt;/p&gt;
&lt;h3 id=&quot;firefox&quot;&gt;Firefox &lt;a class=&quot;w-headline-link&quot; href=&quot;#firefox&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firefox offers good support for WebAssembly, but has disabled support for multi-threading. As a result, you can expect a slower experience with Earth. We look forward to Mozilla bringing back support for multi-threading in future versions. On the upside, Firefox does support dynamic memory allocation.&lt;/p&gt;
&lt;h3 id=&quot;opera&quot;&gt;Opera &lt;a class=&quot;w-headline-link&quot; href=&quot;#opera&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Opera is based on Chromium just as Chrome is, along with upcoming versions of Edge. However, the current version of Opera only offers single-threaded support of WebAssembly. Earth does run in Opera, but at a somewhat degraded experience. Hopefully newer versions of Opera will have support for multi-threading and more robust WebAssembly support.&lt;/p&gt;
&lt;h3 id=&quot;safari&quot;&gt;Safari &lt;a class=&quot;w-headline-link&quot; href=&quot;#safari&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Safari has a strong implementation of WebAssembly, but it lacks full support for WebGL2. Therefore, Earth with WebAssembly does not run in Safari. Specifically, some of our shaders require GLSL 1.2. We hope that Earth will be available on Safari as well, once better support for WebGL2 is added.&lt;/p&gt;
&lt;h2 id=&quot;looking-forward-to-more-adoption-of-webassembly-features&quot;&gt;Looking forward to more adoption of WebAssembly features &lt;a class=&quot;w-headline-link&quot; href=&quot;#looking-forward-to-more-adoption-of-webassembly-features&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s been a long road to make Earth available on the web. About six years ago, we started with an initial &lt;a href=&quot;http://asmjs.org/&quot;&gt;asm.js&lt;/a&gt;-based internal demo that was maintained and expanded over the years. It was then converted into a WebAssembly build of Earth, as WebAssembly became the W3C adopted standard.&lt;/p&gt;
&lt;p&gt;We still have a ways to go for WebAssembly and Earth. Specifically, we&#39;d like to move to the LLVM backend using Emscripten (the toolchain to generate WebAssembly out of C++ code). This change will enable future SIMD support, as well as stronger debugging tools like source maps for native code. Other things we hope to see are adoption of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas&quot;&gt;OffscreenCanvas&lt;/a&gt; and full support for dynamic memory allocation in WebAssembly. But we know we&#39;re on the right track: WebAssembly is the long-term future for Earth on the web.&lt;/p&gt;
&lt;p&gt;Please take a moment to try our &lt;a href=&quot;https://g.co/earth/beta&quot;&gt;beta&lt;/a&gt;. Let us know how it works for you by leaving feedback directly in Earth.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Web components: the secret ingredient helping power the web</title>
    <link href="https://web.dev/web-components-io-2019/"/>
    <updated>2019-06-17T17:00:00-07:00</updated>
    <id>https://web.dev/web-components-io-2019/</id>
    <content type="html">&lt;p&gt;At Google I/O 2019, Kevin Schaaf of the Polymer Project and Caridy Patiño of Salesforce talked about the state of web components.&lt;/p&gt;
&lt;div class=&quot;w-youtube&quot;&gt;
  &lt;iframe class=&quot;w-youtube__embed&quot; src=&quot;https://www.youtube.com/embed/YBwgkr_Sbx0&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;how-popular-are-web-components&quot;&gt;How popular are web components? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-popular-are-web-components&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;ve used the web today, you&#39;ve probably used web components. By our count, somewhere between 5% and 8% of all page loads today use one or more web components. That makes web components one of the most successful new web platform features shipped in the last five years.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/web-components-io-2019/usage-graph.png&quot; alt=&quot;A graph showing that 8% of sites use v1 custom elements. This figure eclipses the 5% highpoint for v0 custom elements.&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;You can find web components on sites you probably use every day, like YouTube and GitHub. They&#39;re also used on many news and publishing sites built with &lt;a href=&quot;http://amp.dev/&quot;&gt;AMP&lt;/a&gt;—AMP components are also web components. And many enterprises are also adopting web components.&lt;/p&gt;
&lt;h2 id=&quot;what-are-web-components&quot;&gt;What are web components? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-are-web-components&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So what are web components? The web components specifications provide a low-level set of APIs that let you extend the browser&#39;s built-in set of HTML tags. Web components provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A common method for creating a component (using standard DOM APIs).&lt;/li&gt;
&lt;li&gt;A common way of receiving and sending data (using properties/events).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Outside of that standard interface, the standards don&#39;t say anything about how a component is actually implemented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What rendering engine it uses to create its DOM.&lt;/li&gt;
&lt;li&gt;How it updates itself based on changes to its properties or attributes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, web components  tell the browser &lt;strong&gt;when&lt;/strong&gt; and &lt;strong&gt;where&lt;/strong&gt; to make a component, but not &lt;strong&gt;how&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Authors can choose functional rendering patterns just like React to build their web components, or they can use declarative templates like you might find in Angular or Vue. As an author you have total freedom to choose the technology you use inside the component, while still maintaining interoperability.&lt;/p&gt;
&lt;h2 id=&quot;what-are-web-components-good-for&quot;&gt;What are web components good for? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-are-web-components-good-for&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key difference between web components and proprietary component systems is  &lt;strong&gt;interoperability&lt;/strong&gt;. Because of their standard interface, you can use web components anywhere you&#39;d use a built-in element like &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because they can be expressed as real HTML, they can be rendered by all the popular frameworks. So your components can be consumed more widely, in a more diverse range of applications, without locking users into any one framework.&lt;/p&gt;
&lt;p&gt;And because the component interface is standard, web components implemented using different libraries can be mixed on the same page. This fact helps future-proof your applications when you update your tech stack. Instead of a giant step-change between one framework and another, where you replace all of your components, you can update your components one at a time.&lt;/p&gt;
&lt;h2 id=&quot;who&#39;s-using-web-components&quot;&gt;Who&#39;s using web components? &lt;a class=&quot;w-headline-link&quot; href=&quot;#who&#39;s-using-web-components&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So for all of these reasons, Web Components are actually finding huge success in a variety of different use cases. Three use cases have been especially popular: content sites, design systems, and enterprise applications.&lt;/p&gt;
&lt;h3 id=&quot;content-sites&quot;&gt;Content sites &lt;a class=&quot;w-headline-link&quot; href=&quot;#content-sites&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web components are the perfect technology for progressively enhancing content, because they can already be output as standard HTML by an untold number of CMS systems.&lt;/p&gt;
&lt;p&gt;AMP is a great example of how quickly and easily Web Components slotted into the publishing industry&#39;s infrastructure for serving content.&lt;/p&gt;
&lt;h3 id=&quot;design-systems&quot;&gt;Design systems &lt;a class=&quot;w-headline-link&quot; href=&quot;#design-systems&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;More and more companies are unifying the way they present themselves using a design system—a set of components and guidelines that define the common look and feel for an organization&#39;s sites and applications. Web components are a great fit here, too.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;img src=&quot;https://web.dev/web-components-io-2019/material-design.png&quot; alt=&quot;The material design homepage, https://material.io.&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Often, designers have to contend with many teams building their own versions of the design system components on top of React, Angular, and all the other frameworks, instead of having a single set of canonical components.&lt;/p&gt;
&lt;p&gt;Web components are the answer—a truly write once, run everywhere component system that still gives app teams freedom to use the framework of their choice&lt;/p&gt;
&lt;p&gt;Companies like ING, EA, and Google are implementing their company&#39;s design language in web components.&lt;/p&gt;
&lt;h3 id=&quot;enterprise:-web-components-at-salesforce&quot;&gt;Enterprise: Web components at Salesforce &lt;a class=&quot;w-headline-link&quot; href=&quot;#enterprise:-web-components-at-salesforce&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web components are also hitting a remarkable stride inside enterprises as a safe, future-proof technology to standardize on. Caridy Patiño, architect for Salesforce&#39;s UI platform, explained why they built their UI platform using web components.&lt;/p&gt;
&lt;p&gt;Salesforce is a collection of applications—many of which came from acquisitions. Each of these may run on its own technology stack. Because they&#39;re built on different stacks, it&#39;s hard to give them all the same look and feel. In addition, Salesforce enables customers to build their own custom applications using the Salesforce platform. So ideally the components should be usable by outside developers, too.&lt;/p&gt;
&lt;p&gt;Salesforce identified a set of needs from customers of their platform:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard, rather than proprietary solutions—so it&#39;s easier to find experienced developers, and quicker to ramp up new developers.&lt;/li&gt;
&lt;li&gt;A common component model—so customizing any Salesforce application works the same way.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They also identified some things customers &lt;em&gt;didn&#39;t&lt;/em&gt; want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Breaking changes on their components and apps. In other words, backwards compatibility was a must.&lt;/li&gt;
&lt;li&gt;Being stuck with old technology, and unable to evolve.&lt;/li&gt;
&lt;li&gt;Being stuck inside a walled garden.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using web components as the basis for the new UI platform met all of these needs, and the result is the new &lt;a href=&quot;https://developer.salesforce.com/docs/component-library/documentation/lwc&quot;&gt;Lightning Web Components&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;get-started-with-web-components&quot;&gt;Get started with web components &lt;a class=&quot;w-headline-link&quot; href=&quot;#get-started-with-web-components&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a lot of great ways to get started with web components.&lt;/p&gt;
&lt;p&gt;If you&#39;re building a web app, consider using some of the many off-the-shelf web components available. Here are just a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google vends its own Material design system as web components: &lt;a href=&quot;https://github.com/material-components/material-components-web-components&quot;&gt;Material Web Components&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://wiredjs.com/&quot;&gt;Wired Elements&lt;/a&gt; are a cool set of web components that feature a sketchy, hand-drawn look.&lt;/li&gt;
&lt;li&gt;There are great special-purpose Web Components like &lt;a href=&quot;https://github.com/GoogleWebComponents/model-viewer&quot;&gt;&lt;model-viewer&gt;&lt;/model-viewer&gt;&lt;/a&gt;, which you can drop into any app to add 3D content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;re developing a design system for your company, or you&#39;re vending a single component or library that you want to be usable in any environment, consider authoring your components using web components. You can use the native web components APIs, but they&#39;re pretty low-level, so there are a number of libraries available to make the process easier.&lt;/p&gt;
&lt;p&gt;To get started building your own components, you can check out LitElement, a web component base class developed by Google that has a great functional rendering experience similar to React.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;geolocation; microphone; camera; midi; encrypted-media&quot; src=&quot;https://glitch.com/embed/#!/embed/lit-element-simple?path=my-component.js&amp;amp;previewSize=0&quot; alt=&quot;lit-element-simple on Glitch&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Other tools and libraries to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stenciljs.com/&quot;&gt;Stencil&lt;/a&gt; is a web-components-first framework. It includes several popular framework features, like JSX and TypeScript&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://angular.io/guide/elements&quot;&gt;Angular Elements&lt;/a&gt; provides a way to wrap Angular components as web components.&lt;/li&gt;
&lt;li&gt;Vue.js &lt;a href=&quot;https://github.com/vuejs/vue-web-component-wrapper&quot;&gt;web component wrapper&lt;/a&gt; provides a way to package Vue components as web components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://open-wc.org/&quot;&gt;open-wc.org&lt;/a&gt; features great getting started information, as well as tips and default configurations for build and development tooling.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/web-components/&quot;&gt;Web Fundamentals&lt;/a&gt; provides primers on the basic web components APIs, and best practices for designing web components.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components&quot;&gt;MDN&lt;/a&gt; provides reference docs for the web components APIs, plus some tutorials. \&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Hero image by Jason Tuinstra on Unsplash.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Editor&#39;s note: The custom elements usage chart has been updated to show the
full monthly usage data, as reported on
&lt;a href=&quot;http://chromestatus.com/&quot;&gt;chromestatus.com&lt;/a&gt;. A previous version of this post
included a graph at a 6-month granularity, without the most recent months. The
V0 &amp;amp; V1 series in the original chart were stacked; they are now shown unstacked
with a total line to remove ambiguity. The abrupt jump in late 2017 is due to a
change in the data collection system for chromestatus.com. This change affected
the stats for all web platform features and resulted in more accurate
measurements going forward.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Use Lighthouse for performance budgets</title>
    <link href="https://web.dev/use-lighthouse-for-performance-budgets/"/>
    <updated>2019-06-13T17:00:00-07:00</updated>
    <id>https://web.dev/use-lighthouse-for-performance-budgets/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse&quot;&gt;Lighthouse&lt;/a&gt; now supports performance budgets. This feature, &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/audits/budgets&quot;&gt;LightWallet&lt;/a&gt;, can be set up in under five minutes and provides feedback on the size and quantity of page resources.&lt;/p&gt;
&lt;h2 id=&quot;install-lighthouse&quot;&gt;Install Lighthouse &lt;a class=&quot;w-headline-link&quot; href=&quot;#install-lighthouse&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LightWallet is available in the command line version of Lighthouse v5+.&lt;/p&gt;
&lt;p&gt;To get started, install Lighthouse:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -g lighthouse&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;create-a-budget&quot;&gt;Create a Budget &lt;a class=&quot;w-headline-link&quot; href=&quot;#create-a-budget&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a file named &lt;code&gt;budget.json&lt;/code&gt;. In this file add the following JSON:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;resourceSizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;resourceType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;script&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;125&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;resourceType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;total&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;resourceCounts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;resourceType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;third-party&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example &lt;code&gt;budget.json&lt;/code&gt; file sets three separate budgets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A budget of 125 KB for the total amount of JavaScript on the page.&lt;/li&gt;
&lt;li&gt;A budget of 300 KB for the overall size of the page.&lt;/li&gt;
&lt;li&gt;A budget of 10 requests for the numer of requests made to third-party origins.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can set budgets for any of the following resource types: &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;media&lt;/code&gt;, &lt;code&gt;other&lt;/code&gt;,&lt;code&gt;script&lt;/code&gt;, &lt;code&gt;stylesheet&lt;/code&gt;, &lt;code&gt;third-party&lt;/code&gt;, and &lt;code&gt;total&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;run-lighthouse&quot;&gt;Run Lighthouse &lt;a class=&quot;w-headline-link&quot; href=&quot;#run-lighthouse&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Lighthouse using the &lt;code&gt;--budget-path&lt;/code&gt; flag. This flag tells Lighthouse the location of your budget file.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;lighthouse https://example.com --budget-path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;./budget.json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: A budget file does not have to be named &lt;code&gt;budget.json&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;view-the-results&quot;&gt;View the Results &lt;a class=&quot;w-headline-link&quot; href=&quot;#view-the-results&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If LightWallet has been configured correctly, the Lighthouse report will contain a &lt;strong&gt;Budgets&lt;/strong&gt; section within the &lt;strong&gt;Performance&lt;/strong&gt; category.&lt;/p&gt;
&lt;img src=&quot;https://web.dev/use-lighthouse-for-performance-budgets/lightwallet.png&quot; class=&quot;w-screenshot&quot; alt=&quot;&#39;Budgets&#39; section of the Lighthouse report&quot;&gt;
&lt;p&gt;In the JSON version of the Lighthouse report, Lightwallet results can be found within the audit findings for the &lt;code&gt;performance-budget&lt;/code&gt; audit.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>The value of speed</title>
    <link href="https://web.dev/value-of-speed/"/>
    <updated>2019-06-12T17:00:00-07:00</updated>
    <id>https://web.dev/value-of-speed/</id>
    <content type="html">&lt;p&gt;After a lot of hard work, you&#39;ve done it. You&#39;ve made your company&#39;s site
noticeably faster. Now it&#39;s time for the fun part: showing stakeholders how much
extra revenue your work has generated!&lt;/p&gt;
&lt;p&gt;In this post we&#39;ll walk through how to do that by calculating the &lt;em&gt;relative
mobile conversion rate&lt;/em&gt;. This metric is useful because it quantifies the
effects of site improvements while excluding external factors like marketing
campaigns, which can obscure your findings. Let&#39;s get started!&lt;/p&gt;
&lt;h2 id=&quot;relative-mobile-conversion-rate-(rel-mcvr)&quot;&gt;Relative Mobile Conversion Rate (Rel mCvR) &lt;a class=&quot;w-headline-link&quot; href=&quot;#relative-mobile-conversion-rate-(rel-mcvr)&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A site&#39;s conversion rate can be influenced by site characteristics—like speed
and usability—and by external factors—like marketing campaigns, seasonal events,
and the mix of marketing channels.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;A &lt;em&gt;conversion&lt;/em&gt; happens when a site visitor becomes a customer. What counts
as a conversion will vary depending on the nature of the site. Buying a
product, subscribing to a service, registering as a user, or even just
viewing a particular page could be considered a conversion. Check with your
marketing team if you&#39;re not sure how conversions are counted in your company.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Since you&#39;re interested in how site speed affects conversions, the mobile site
is most relevant—that&#39;s where you&#39;re most likely to see the benefits of
speed improvements. Rather than looking at the mobile conversion rate alone,
though, you&#39;ll be analyzing the &lt;em&gt;relative&lt;/em&gt; mobile conversion rate (Rel mCvr),
which is calculated by dividing the mobile conversion rate by the desktop
conversion rate. This approach reduces the noise from external factors, which
tend to affect both desktop and mobile, and makes it easier to see whether any
increases in the mobile site&#39;s effectiveness were actually caused by the speed
improvements.&lt;/p&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/value-of-speed/relative-versus-absolute.jpg&quot; alt=&quot;Table showing comparison of mobile/desktop conversion rate and relative mobile conversion rate&quot;&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
Rel mCvR is influenced not only by speed, but also by other site
characteristics like usability. If other big changes were made to the site
during the period you want to analyze, you won&#39;t be able to measure the effects
of speed improvements on their own, but you can show the benefits of all the
improvements as a group.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;doing-the-analysis&quot;&gt;Doing the analysis &lt;a class=&quot;w-headline-link&quot; href=&quot;#doing-the-analysis&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have access to your site&#39;s page analytics data, you can follow along
using that. This walkthrough uses Google Analytics to get site
data. If you don&#39;t have a Google Analytics account, you can learn how to set one
up at &lt;a href=&quot;https://support.google.com/analytics/answer/1008015?hl=en&quot;&gt;Get started with Analytics&lt;/a&gt;.
Or, if you just want to see how Rel mCvR is calculated, you can get the sample
data set shown below from
&lt;a href=&quot;https://docs.google.com/spreadsheets/d/1Mxmy1luPBOvJneSM9g2NZt6yHKx8xkpBXygOpm4LgNM/edit?usp=sharing&quot;&gt;this Google Sheet&lt;/a&gt;
and skip steps 1–3.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; First get the speed and conversion data for the mobile site.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open
&lt;a href=&quot;https://analytics.google.com/analytics/web/template?uid=NPvTU2zoTf2JFsHi6-auyQ&quot;&gt;this custom report&lt;/a&gt;
in Google Analytics, select your site from the &lt;strong&gt;Select a view&lt;/strong&gt;
drop-down list, and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Once the report has been generated, click &lt;strong&gt;Select a metric &amp;gt; Metric Group &amp;gt; Ecommerce Conversion Rate&lt;/strong&gt;.
&lt;br&gt;&lt;br&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/value-of-speed/conversion-rate.png&quot; alt=&quot;Screenshot of Google Analytics: select a metric, Metric Group, Ecommerce Conversion Rate&quot;&gt;
&lt;br&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Week&lt;/strong&gt; view for the dataset.
&lt;br&gt;&lt;br&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/value-of-speed/landing-pages-week.jpg&quot; alt=&quot;Screenshot of Google Analytics: select a week&quot;&gt;
&lt;br&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;Select a time period that covers 2–3 months before the speed
optimization and 2–3 months after. (In our example, that&#39;s January 2018
through October 2018.)
&lt;br&gt;&lt;br&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/value-of-speed/analytics-start-date.jpg&quot; alt=&quot;Screenshot of Google Analytics: select a start date&quot;&gt;
&lt;br&gt;&lt;br&gt;&lt;/li&gt;
&lt;li&gt;Export the report to &lt;a href=&quot;http://sheets.google.com/&quot;&gt;Google Sheets&lt;/a&gt; or
your preferred spreadsheet application.
&lt;br&gt;&lt;br&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/value-of-speed/export-google-sheets.jpg&quot; alt=&quot;Screenshot of Google Analytics: export to Google Sheets&quot; style=&quot;max-width: 400px; width: 100%&quot;&gt;
&lt;br&gt;&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Now get the speed and conversion data for the desktop site. Open
&lt;a href=&quot;https://analytics.google.com/analytics/web/template?uid=X21Nb_soQp69U_ylfNmLvg&quot;&gt;this custom report&lt;/a&gt;
in Google Analytics and repeat the process for step 1 to export the data to
a spreadsheet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Copy the &lt;em&gt;Ecommerce Conversion Rate&lt;/em&gt; column in the desktop report
spreadsheet and insert it next to the &lt;em&gt;Ecommerce Conversion Rate&lt;/em&gt; in the
mobile report spreadsheet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; To calculate the Rel mCvR, create a new column titled &lt;em&gt;Relative Mobile Conversion Rate (Rel mCvR)&lt;/em&gt;.
Then add a formula to divide the mobile conversion rate by the desktop conversion rate.
&lt;br&gt;&lt;br&gt;
&lt;img src=&quot;https://web.dev/value-of-speed/relative-rate-formula.jpg&quot; alt=&quot;Screenshot: spreadsheet cells showing relative rate formula&quot;&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; Create a chart based on the &lt;em&gt;Mobile Avg. Page Load Time (sec)&lt;/em&gt; and
&lt;em&gt;Relative Mobile Conversion Rate (Rel mCvR)&lt;/em&gt; columns. To do that in Google
Sheets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Insert &amp;gt; Chart&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Chart Editor&lt;/strong&gt; pane, select the &lt;strong&gt;Line chart&lt;/strong&gt; chart type.&lt;/li&gt;
&lt;li&gt;Select the &lt;em&gt;Week Index&lt;/em&gt;, &lt;em&gt;Mobile Avg. Page Load Time (sec)&lt;/em&gt;, and
&lt;em&gt;Relative Mobile Conversion Rate (Rel mCvR)&lt;/em&gt; columns as the data ranges.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Customize&lt;/strong&gt; tab and then select &lt;strong&gt;Series&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Series selector&lt;/strong&gt; drop-down list, select &lt;strong&gt;Relative
Mobile Conversion Rate (Rel mCvR)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Axis&lt;/strong&gt; drop-down list select &lt;strong&gt;Right axis&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If you like, click &lt;strong&gt;Chart &amp;amp; axis titles&lt;/strong&gt; to customize the chart title
and the left and right &lt;em&gt;y&lt;/em&gt;-axis labels.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should now have a chart that looks something like this:
&lt;img src=&quot;https://web.dev/value-of-speed/mobile-versus-relative-chart.jpg&quot; alt=&quot;Chart: mobile load time versus relative mobile conversion rate&quot;&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; Using the chart, identify a period before the speed optimization
(when load times were high) and a period after the speed optimization (when
load times should be lower) that you want to analyze. In this example, you
would compare eight weeks in Jan–Feb to eight weeks in Aug–Sept.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 7:&lt;/strong&gt; In a new sheet, calculate the average load time and rel mCvR for the
two periods. Then add the revenue coming from mobile visitors during the
period after the speed optimization (Aug–Sept in the example). You can find
revenue data in Google Analytics under the section &lt;strong&gt;Audience &amp;gt; Mobile &amp;gt;
Overview&lt;/strong&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/value-of-speed/period-revenue.jpg&quot; alt=&quot;Revenue data for January through September showing Rel mCvR&quot; style=&quot;max-width: 600px; width: 100%&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Step 8:&lt;/strong&gt; Now calculate what the revenue would have been if Rel mCvR had not
improved. Do this by dividing the revenue (€1,835,962) by the current Rel
mCvR (51%) and multiplying by the Rel mCvR for the period before the speed
optimization (42%).&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/value-of-speed/revenue-would-have-been.jpg&quot; alt=&quot;Screenshot: spreadsheet cells showing formula for revenue without Rel mCvR improvements&quot; style=&quot;max-width: 600px; width: 100%&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Step 9:&lt;/strong&gt; Subtract the revenue that the company earned from what it would have
earned if Rel mCvR had not improved.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/value-of-speed/extra-revenue-formula.jpg&quot; alt=&quot;Screenshot: spreadsheet cells showing extra revenue formula&quot; style=&quot;max-width: 600px; width: 100%&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;In this example, the company earned an additional €323,993 in eight weeks thanks
to Rel mCvR improving—that is, thanks to the mobile site becoming faster.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/value-of-speed/thanks-to-increased.jpg&quot; alt=&quot;Screenshot: spreadsheet cells showing extra revenue due to Rel mCvR improvements&quot; style=&quot;max-width: 600px; width: 100%&quot;&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;things-to-consider-when-analyzing-rel-mcvr&quot;&gt;Things to consider when analyzing Rel mCvR &lt;a class=&quot;w-headline-link&quot; href=&quot;#things-to-consider-when-analyzing-rel-mcvr&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;As noted above, other changes to the site, such as UX improvements, can
influence Rel mCvR. Check that speed was the only big change to the site during
the period you want to study. If there &lt;em&gt;were&lt;/em&gt; other changes, Rel mCvR can tell
you the effect of the changes as a group, but not of an individual change.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Watch out for any changes or events that affected the desktop site but not the
mobile site—they can skew your results. If you discover any desktop-only
changes, omit the affected period from your analysis.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You might wonder whether an increase in Rel mCvR is caused by a shift in
conversions from desktop to mobile rather than an increase in conversions
overall. While there is likely to be some change in the mix of desktop and
mobile conversions due to speed improvements, keep in mind that Rel mCvR
calculates the mobile conversion rate relative to the desktop conversion rate.
So, you only see an increase in Rel mCvR when mCvR goes up more than dCvR. In
other words, when doing this calculation you&#39;re already counting low, which
means you have a safety margin that can compensate for any shifts in the
channel mix.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summing-up&quot;&gt;Summing up &lt;a class=&quot;w-headline-link&quot; href=&quot;#summing-up&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While it has some limitations, Rel mCvR is a great low-cost way to estimate how
much a speed optimization increased revenue without, for example, having to run
server-side or slow-down tests. And quantifying the relationship between
performance and revenue can help you demonstrate the value of development
projects whose benefits might not be immediately clear to non-technical
stakeholders.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a class=&quot;w-headline-link&quot; href=&quot;#next-steps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse&quot;&gt;Discover performance opportunities with Lighthouse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/performance-budgets-101&quot;&gt;Performance budgets 101&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Aircraft instrument panel image by
&lt;a href=&quot;https://unsplash.com/photos/MHIw0nSxCR4&quot;&gt;Arie Wubben&lt;/a&gt; on
&lt;a href=&quot;https://unsplash.com/&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Updates to the Web Payments APIs</title>
    <link href="https://web.dev/web-payments-updates/"/>
    <updated>2019-06-12T17:00:00-07:00</updated>
    <id>https://web.dev/web-payments-updates/</id>
    <content type="html">&lt;p&gt;Web Payments have been publicly available in browsers since 2016. The core
feature—the &lt;a href=&quot;https://www.w3.org/TR/payment-request/&quot;&gt;Payment Request API&lt;/a&gt;—is
now available across multiple browsers: Chrome, Safari, Edge and soon Firefox.
If you&#39;re new to Web Payments take a look at the &lt;a href=&quot;https://developers.google.com/web/fundamentals/payments/?hl=en&quot;&gt;&amp;quot;Web Payments
Overview&amp;quot;&lt;/a&gt; to
get started.&lt;/p&gt;
&lt;p&gt;Since the launch of the Payment Request API and the &lt;a href=&quot;https://w3c.github.io/payment-handler/&quot;&gt;Payment Handler
API&lt;/a&gt;, there have been quite a few
changes made to their respective specifications. These changes won&#39;t break your
working code, but we recommend that you look out for them. This post summarizes
those updates and will continue accumulating those API changes.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;If you want to subscribe to further upcoming changes to
Chrome&#39;s Web Payments implementation, please join the &lt;a href=&quot;https://groups.google.com/a/chromium.org/forum/#!forum/paymentrequest&quot;&gt;Public Payment Request
Announcements
group&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Alternatively, join the
&lt;a href=&quot;https://spectrum.chat/web-payments&quot;&gt;public Web Payments community&lt;/a&gt; for the same
updates. You may ask questions there as well.&lt;/p&gt;
&lt;p&gt;If you want to check older updates to Chrome, head over to
&lt;a href=&quot;https://developers.google.com/web/updates/2017/01/payment-request-updates&quot;&gt;&amp;quot;Changes in the Payment Request API&amp;quot; article at Web Fundamentals&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;new-method:-hasenrolledinstrument()&quot;&gt;New method: &lt;code&gt;hasEnrolledInstrument()&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#new-method:-hasenrolledinstrument()&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the previous version of the Payment Request API, &lt;code&gt;canMakePayment()&lt;/code&gt; was used
to check for the user&#39;s presence of the user&#39;s payment instrument. In a recent
update to the spec, &lt;code&gt;canMakePayment()&lt;/code&gt; has been replaced with
&lt;a href=&quot;https://w3c.github.io/payment-request/#hasenrolledinstrument-method&quot;&gt;&lt;code&gt;hasEnrolledInstrument()&lt;/code&gt;&lt;/a&gt;
without changing its functionality.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://chromestatus.com/feature/5646573451083776&quot;&gt;&lt;code&gt;hasEnrolledInstrument()&lt;/code&gt;&lt;/a&gt;
method has &lt;a href=&quot;https://chromestatus.com/feature/5646573451083776&quot;&gt;consensus from all major browsers&lt;/a&gt;.
Chrome has implemented it in version 74 and both &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=197386&quot;&gt;Webkit&lt;/a&gt;
and &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1528663&quot;&gt;Gecko&lt;/a&gt; have tracking
bugs but have not yet implemented the method as of June 2019.&lt;/p&gt;
&lt;p&gt;To use the new &lt;code&gt;hasEnrolledInstrument()&lt;/code&gt; method, replace code that looks like
this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Checking for instrument presence.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canMakePayment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleInstrumentPresence&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With code that looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Checking for instrument presence.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hasEnrolledInstrument&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// hasEnrolledInstrument() is available.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasEnrolledInstrument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleInstrumentPresence&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canMakePayment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleInstrumentPresence&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;canmakepayment()-no-longer-checks-for-instrument-presence&quot;&gt;&lt;code&gt;canMakePayment()&lt;/code&gt; no longer checks for instrument presence &lt;a class=&quot;w-headline-link&quot; href=&quot;#canmakepayment()-no-longer-checks-for-instrument-presence&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because &lt;code&gt;hasEnrolledInstrument()&lt;/code&gt; now handles checking for the user&#39;s payment
instrument,
&lt;a href=&quot;https://w3c.github.io/payment-request/#canmakepayment-method&quot;&gt;&lt;code&gt;canMakePayment()&lt;/code&gt;&lt;/a&gt;
has been updated to only check for payment app availability.&lt;/p&gt;
&lt;p&gt;This change to &lt;code&gt;canMakePayment()&lt;/code&gt; is bound to the implementation of
&lt;code&gt;hasEnrolledInstrument()&lt;/code&gt;. As of June 2019, it is implemented in Chrome 74 but
not in any other browsers. Be sure to check if the &lt;code&gt;hasEnrolledInstrument()&lt;/code&gt;
method is available in the user&#39;s browser before attempting to use it.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Checking for payment app availability without checking for instrument presence.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hasEnrolledInstrument&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// `canMakePayment()` behavior change corresponds to&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// `hasEnrolledInstrument()` availability.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canMakePayment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handlePaymentAppAvailable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cannot check for payment app availability without checking for instrument presence.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;languagecode-removed-from-basic-card-payment-method&quot;&gt;&lt;code&gt;languageCode&lt;/code&gt; removed from &lt;code&gt;basic-card&lt;/code&gt; payment method &lt;a class=&quot;w-headline-link&quot; href=&quot;#languagecode-removed-from-basic-card-payment-method&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;PaymentAddress.languageCode&lt;/code&gt; has been removed from the shipping addresses and
billing addresses for &lt;code&gt;basic-card&lt;/code&gt;. The billing addresses of other payment
methods (such as Google Pay) are not affected.&lt;/p&gt;
&lt;p&gt;This change has been implemented &lt;a href=&quot;https://chromestatus.com/features/4992562146312192&quot;&gt;in Chrome 74, Firefox, and Safari&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;paymentrequest.show()-now-takes-an-optional-detailspromise&quot;&gt;&lt;code&gt;PaymentRequest.show()&lt;/code&gt; now takes an optional &lt;code&gt;detailsPromise&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#paymentrequest.show()-now-takes-an-optional-detailspromise&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://w3c.github.io/payment-request/#show-method&quot;&gt;&lt;code&gt;PaymentRequest.show()&lt;/code&gt;&lt;/a&gt;
allows a merchant to present a payment request UI before the final total is
known. The merchant has ten seconds to resolve the &lt;code&gt;detailsPromise&lt;/code&gt; before a
timeout. This feature is intended for a quick server-side roundtrip.&lt;/p&gt;
&lt;p&gt;This feature has shipped in Chrome 75 and Safari.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Not implemented in Chrome 74 and older.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// There&#39;s no way to feature-detect this, so check a few&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// older versions of Chrome that you&#39;re seeing hit your servers.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/Chrome\/((6[0-9])|(7[0-4]))/g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Supported in Chrome 75+.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolveDetailsPromise&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rejectDetailsPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Find out the exact total amount and call&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// `resolveDetailsPromise(details)`.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Use a 3 second timeout as an example.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;resolveDetailsPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;details&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3 seconds.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handleError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;paymentrequestevent.changepaymentmethod()&quot;&gt;&lt;code&gt;PaymentRequestEvent.changePaymentMethod()&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#paymentrequestevent.changepaymentmethod()&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Payment Handler API feature
&lt;a href=&quot;https://chromestatus.com/feature/5698314223747072&quot;&gt;&lt;code&gt;PaymentRequestEvent.changePaymentMethod()&lt;/code&gt;&lt;/a&gt;
allows payment handlers (e.g., Google Pay) to trigger the
&lt;a href=&quot;https://w3c.github.io/payment-request/#dom-paymentmethodchangeevent&quot;&gt;&lt;code&gt;onpaymentmethodchange&lt;/code&gt;&lt;/a&gt;
event handler. &lt;code&gt;changePaymentMethod()&lt;/code&gt; returns a promise that resolves to a
&lt;a href=&quot;https://w3c.github.io/payment-handler/#dom-paymentmethodchangeresponse&quot;&gt;merchant
response&lt;/a&gt;
with updated price information (e.g., tax recalculation).&lt;/p&gt;
&lt;p&gt;Both &lt;code&gt;PaymentRequestEvent.changePaymentMethod()&lt;/code&gt; and the &lt;code&gt;paymentmethodchange&lt;/code&gt;
event are available in Chrome 76 and Webkit has implemented the
&lt;a href=&quot;https://webkit.org/blog/9167/whats-new-in-the-payment-request-api-for-apple-pay/&quot;&gt;&lt;code&gt;paymentmethodchange&lt;/code&gt; event in its Technology
Preview&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// In service worker context, `self` is the global object.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;paymentrequest&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;confirmPaymentFunction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rejectPaymentFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;changePaymentMethod &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;// Not implemented in this version of Chrome.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Notify merchant that the user selected a different payment method.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changePaymentMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://paymentapp.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;country&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;US&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseFromMerchant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseFromMerchant &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// Merchant ignored the &#39;paymentmethodchange&#39; event.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token function&quot;&gt;handleResponseFromMerchant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseFromMerchant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token function&quot;&gt;handleError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Merchant example:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onpaymentmethodchange &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Feature not available in this browser.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;paymentmethodchange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateDetailsBasedOnPaymentMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; paymentDetails&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Try it yourself&lt;/strong&gt;! We&#39;ve put together &lt;a href=&quot;https://rsolomakhin.github.io/pr/apps/pmc/&quot;&gt;an example so you can experiment with this API&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;improved-local-development&quot;&gt;Improved local development &lt;a class=&quot;w-headline-link&quot; href=&quot;#improved-local-development&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chrome 76 adds two small improvements for developer productivity. If your local
development environment uses a self-signed certificate, you can now use the
&lt;code&gt;--ignore-certificate-errors&lt;/code&gt; command line flag to make Chrome allow Web
Payments APIs in your development environment. If you develop using a local web
server that does not support HTTPS, you can also use the
&lt;code&gt;--unsafely-treat-insecure-origin-as-secure=&amp;lt;origin&amp;gt;&lt;/code&gt; flag to make Chrome treat
the HTTP origin as HTTPS.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How YouTube improved video performance with the Media Capabilities API</title>
    <link href="https://web.dev/youtube-media-capabilities/"/>
    <updated>2019-06-11T17:00:00-07:00</updated>
    <id>https://web.dev/youtube-media-capabilities/</id>
    <content type="html">&lt;p&gt;In an experiment with the Media Capabilities API, YouTube saw a 7.1% increase in
MTBR with only a 0.4% decrease in average resolution of videos served.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--key-term&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key Term:&lt;/strong&gt;
MTBR (Mean Time Between Rebuffers) is total play time divided by the number of
rebuffering events.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The Problem &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Typically, media sites have several variants of each video that they can present
to users, encoded in different frame rates, resolutions, and codecs. Until
recently, web developers had to rely solely on &lt;code&gt;isTypeSupported()&lt;/code&gt; or
&lt;code&gt;canPlayType()&lt;/code&gt; to determine whether each variant could be played in an
individual user&#39;s browser.
While this told the developer whether media could be played at all, it didn&#39;t
provide an indication of playback quality, such as whether there would be frame
drops or device battery drain. Without this information, developers either had
to create their own heuristics or just assume that if a device could play a
codec/resolution combination, it could do so smoothly and with power efficiency.
For users with less capable devices, this often led to a poor experience.&lt;/p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The Solution &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-solution&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://wicg.github.io/media-capabilities/&quot;&gt;Media Capabilities&lt;/a&gt; API allows
websites to get more information about the client&#39;s video decode performance and
make an informed decision about which codec and resolution to deliver to the
user. Specifically, the API provides the developer with an estimate of the
smoothness and power efficiency of a particular codec and resolution
combination. This allows the developer to avoid scenarios where the client is
likely to have a poor playback experience.&lt;/p&gt;
&lt;p&gt;In Chrome, the Media Capabilities API uses metrics from previous playbacks to
predict whether future playbacks in the same codec and at the same resolution
will be smoothly decoded.&lt;/p&gt;
&lt;h2 id=&quot;youtube-case-study&quot;&gt;YouTube Case Study &lt;a class=&quot;w-headline-link&quot; href=&quot;#youtube-case-study&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;YouTube used the &lt;a href=&quot;https://wicg.github.io/media-capabilities/&quot;&gt;Media
Capabilities&lt;/a&gt; API to prevent their
adaptive bitrate algorithm from automatically selecting resolutions that a
device could not play smoothly.&lt;/p&gt;
&lt;p&gt;Users who were part of the experimental group collectively saw less frequent
rebuffers (the mean time between rebuffers, or MTBR, increased by 7.1%) while
the average resolution, measured by video height, served to the aggregate group
only declined by 0.4%. The substantial increase in the MTBR with the small corresponding reduction in average resolution indicates that this change
significantly improved quality for a small subset of users who previously had a
poor experience.&lt;/p&gt;
&lt;h2 id=&quot;implementing-media-capabilities-api-on-your-site&quot;&gt;Implementing Media Capabilities API on your site &lt;a class=&quot;w-headline-link&quot; href=&quot;#implementing-media-capabilities-api-on-your-site&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://googlechrome.github.io/samples/media-capabilities/decoding-info.html&quot;&gt;official
sample&lt;/a&gt;
to see how the Decoding Info API works.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How can performance improve conversion?</title>
    <link href="https://web.dev/how-can-performance-improve-conversion/"/>
    <updated>2019-06-10T17:00:00-07:00</updated>
    <id>https://web.dev/how-can-performance-improve-conversion/</id>
    <content type="html">&lt;p&gt;In our other e-commerce guides you have learned about &lt;a href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/&quot;&gt;what you should measure
to improve performance&lt;/a&gt;, and
&lt;a href=&quot;https://web.dev/how-can-performance-improve-conversion/how-to-report-metrics/&quot;&gt;how to measure and report metrics to build a performance culture&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/what-should-you-measure-to-improve-performance/funnel.png&quot; alt=&quot;A conversion funnel going from discover to engage to convert to re-engage.&quot; style=&quot;max-width: 600px; width: 100%;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    A conversion funnel.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this guide we&#39;ll address the different ways in which a website should be optimized for performance to yield maximum conversions at the end of the funnel.&lt;/p&gt;
&lt;h2 id=&quot;discovery&quot;&gt;Discovery &lt;a class=&quot;w-headline-link&quot; href=&quot;#discovery&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;New users discover a website in most cases through organic search, social sharing, website links or paid campaigns. Some important discovery mechanisms are directly affected by website performance. Website crawlers may have difficulty indexing sites that are slow to load or have extensive &lt;a href=&quot;https://developers.google.com/search/docs/guides/dynamic-rendering&quot;&gt;client side rendering and Javascript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Speed can also be a direct ranking factor, for example on &lt;a href=&quot;https://webmasters.googleblog.com/2018/01/using-page-speed-in-mobile-search.html&quot;&gt;web search&lt;/a&gt;, &lt;a href=&quot;https://developers.google.com/web/updates/2018/07/search-ads-speed#the_mobile_speed_score_for_ads_landing_pages&quot;&gt;ad campaigns&lt;/a&gt; or &lt;a href=&quot;https://newsroom.fb.com/news/2017/08/news-feed-fyi-showing-you-stories-that-link-to-faster-loading-webpages/&quot;&gt;social networks&lt;/a&gt;.
Keep in mind that new users who discover your website will get an uncached first load, so basically the worst possible experience. This can be especially frustrating if good money was spent to get the user to the website, just to see her dropping off due to a long first load.&lt;/p&gt;
&lt;p&gt;Make sure to use appropriate tools as described in &lt;a href=&quot;https://web.dev/fast&quot;&gt;Fast Load Times&lt;/a&gt; to optimize towards a first load, because first impressions matter—if the first load is too slow, the user may never see the optimized second load or stay around to look at your products. In general &lt;a href=&quot;https://developer.akamai.com/blog/2015/09/01/mobile-web-performance-monitoring-conversion-rate&quot;&gt;loading times of a website map very well to bounce rates&lt;/a&gt;, which in turn often correlate well with conversions.&lt;/p&gt;
&lt;h2 id=&quot;engagement&quot;&gt;Engagement &lt;a class=&quot;w-headline-link&quot; href=&quot;#engagement&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After getting users to your site, you need to keep them engaged with your content, which you can verify in your analytics of choice by looking at session length, time-on-page, pages-per-session and general &lt;a href=&quot;https://support.google.com/analytics/answer/1709395?hl=en&quot;&gt;user flows&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/how-can-performance-improve-conversion/ga_flow.png&quot; alt=&quot;A Google Analytics dashboard shows the number of users that drop off from starting page to first and second interactions.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    A user flow through the funnel as seen by Google Analytics.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Besides various UX best practices, a smooth, fast and responsive experience is key here. While optimizing a website for discovery means optimizing for first load, optimizing for engagement means fast navigations and fast repeat loads. Analyze at which steps of the flow users drop out, and then relate back to speed metrics for these navigations. This can be analyzed for example via &lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-Sample-scripts&quot;&gt;WebPageTest&lt;/a&gt;, &lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md&quot;&gt;Puppeteer&lt;/a&gt; or via the Chrome DevTools &lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/#record&quot;&gt;Record&lt;/a&gt; feature. We will show you more examples of those in the following guides.&lt;/p&gt;
&lt;h2 id=&quot;conversion&quot;&gt;Conversion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conversion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While website conversions are often bound to good discovery paired with great engagement, there are a couple of additional points to remember. Users expect hero images to &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#tracking_fmp_using_hero_elements&quot;&gt;load fast&lt;/a&gt;, call-to-action buttons should be rendered and &lt;a href=&quot;https://developers.google.com/web/updates/2018/05/lighthouse#all_text_remains_visible_during_web_font_loads&quot;&gt;labeled&lt;/a&gt; quickly, page should be &lt;a href=&quot;https://developers.google.com/web/updates/2018/05/first-input-delay&quot;&gt;responsive&lt;/a&gt; and &lt;a href=&quot;https://css-tricks.com/content-jumping-avoid/&quot;&gt;layout jumps&lt;/a&gt; should be avoided. A user won&#39;t buy anything if she can&#39;t click the &lt;em&gt;Buy Now&lt;/em&gt; button due to busy CPU or a jumping or unlabeled button.
In general it&#39;s best to measure and track the time-to-action towards a conversion or a subgoal for it, for example the median time it takes shoppers to get from landing on your site, to viewing a product, to completing payment .&lt;/p&gt;
&lt;h2 id=&quot;re-engagement&quot;&gt;Re-Engagement &lt;a class=&quot;w-headline-link&quot; href=&quot;#re-engagement&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It turns out that only 2% of users &lt;a href=&quot;https://retargeter.com/what-is-retargeting-and-how-does-it-work/&quot;&gt;convert on first visit&lt;/a&gt;, so it is important to get the other 98% to come back, and re-engage them with your content. Modern websites have different ways to do this, for example via mail, tailored display ads in &lt;a href=&quot;https://support.google.com/google-ads/answer/2453998?hl=en&quot;&gt;remarketing&lt;/a&gt; or notifications. This works best if the flow from reengagement to website is as smooth as possible. Unfortunately this is not always the case, as for example mail apps often open links in their in-app webview, slowing down page load and complicating logins through different cache and cookie storage.
Make sure to optimize for fast repeat loads and smooth UX flows to increase chances of reengagement.&lt;/p&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap &lt;a class=&quot;w-headline-link&quot; href=&quot;#recap&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;E-commerce sites always strive for conversions, which are at the end of a purchase funnel. Every step along the funnel needs to be optimized for website speed to minimize bounce rates and drop-offs, and for each step there are different things to optimize, different pitfalls and culprits:&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/what-should-you-measure-to-improve-performance/funnel.png&quot; alt=&quot;A conversion funnel going from discover to engage to convert to re-engage.&quot; style=&quot;max-width: 600px; width: 100%;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    An e-commerce funnel showing which metric to optimize in which step.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To learn more, be sure to check out the other posts in this series on &lt;a href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/&quot;&gt;measuring
to improve performance&lt;/a&gt;, and
&lt;a href=&quot;https://web.dev/how-can-performance-improve-conversion/how-to-report-metrics/&quot;&gt;how report metrics to build a performance culture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image by Campaign Creators on Unsplash&lt;/em&gt;.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>The Layout Instability API</title>
    <link href="https://web.dev/layout-instability-api/"/>
    <updated>2019-06-10T17:00:00-07:00</updated>
    <id>https://web.dev/layout-instability-api/</id>
    <content type="html">&lt;p&gt;Have you ever been reading an article online when something suddenly changes on
the page? Without warning, the text moves, and you&#39;ve lost your place.&lt;/p&gt;
&lt;p&gt;Or even worse: you&#39;re about to tap a link or a button, but in the instant
before your finger lands—&lt;strong&gt;BOOM&lt;/strong&gt;—the link moves, and you end up
clicking something else!&lt;/p&gt;
&lt;p&gt;Most of the time these kinds of experiences are just a mild annoyance or
frustration. But in some cases, they can actually cause real damage or harm.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;video autoplay=&quot;&quot; controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot; poster=&quot;https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability-poster.png&quot; width=&quot;658&quot; height=&quot;510&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability.webm&quot; type=&quot;video/webm; codecs=vp8&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability.mp4&quot; type=&quot;video/mp4; codecs=h264&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    A screencast illustrating how layout instability can negatively affect users.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Unexpected movement of page content usually happens because resources are
loaded asynchronously or DOM elements get dynamically added to the page above
existing content. The culprit might be an image or video with unknown
dimensions, a font that renders larger or smaller than its fallback, or a
third-party ad or widget that dynamically resizes itself.&lt;/p&gt;
&lt;p&gt;What makes this issue even more problematic is that how a site functions in
development is often quite different from how users experience it in
production: personalized or third-party content often doesn&#39;t behave the same
in development as it does in production, test images are often already in the
developer&#39;s browser cache, and API calls that run locally are often so fast
that the delay isn&#39;t noticeable.&lt;/p&gt;
&lt;p&gt;The first step toward properly solving this problem is to give developers the
tools to measure it and understand how often it&#39;s occurring for real users. The
&lt;a href=&quot;https://github.com/WICG/layout-instability&quot;&gt;Layout Instability API&lt;/a&gt;, currently
being incubated in the &lt;a href=&quot;https://www.w3.org/community/wicg/&quot;&gt;WICG&lt;/a&gt;, aims to
address this.&lt;/p&gt;
&lt;h2 id=&quot;how-is-layout-instability-determined&quot;&gt;How is layout instability determined? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-is-layout-instability-determined&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Layout Instability is determined by calculating a &lt;em&gt;layout shift&lt;/em&gt; score every
time the browser renders a new frame. Developers and analytics providers can
then monitor these scores in the wild, identify the culprits, and improve the
experience for their users.&lt;/p&gt;
&lt;h3 id=&quot;layout-shift-defined&quot;&gt;Layout shift defined &lt;a class=&quot;w-headline-link&quot; href=&quot;#layout-shift-defined&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For each element that&#39;s visible in the viewport, if the element&#39;s block-start
and inline-start positions (e.g., its top and left position in the default
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode&quot;&gt;writing mode&lt;/a&gt;)
relative to its &lt;a href=&quot;https://www.w3.org/TR/CSS2/visudet.html#containing-block-details&quot;&gt;containing
element&lt;/a&gt; has
changed between the frame currently being rendered and the frame that was
previously rendered, the element is identified as unstable.&lt;/p&gt;
&lt;p&gt;The union of the visible areas of all unstable elements for the previous frame
&lt;em&gt;and&lt;/em&gt; the current frame—as a fraction of the total area of the
viewport—is the &lt;strong&gt;layout shift score&lt;/strong&gt; for the current frame.&lt;/p&gt;
&lt;p&gt;Here are a few examples of how the layout shift score is calculated:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/layout-instability-api/layout-shift-1.png&quot; alt=&quot;Layout shift example with one unstable element&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the image above there&#39;s an element that takes up half of the viewport in one
frame. Then, in the next frame, the element shifts down by 25% of the viewport
height. The red, dotted rectangle indicates the union of the element&#39;s visible
area in both frames, which, in this case, is 75% of the total viewport, so its
layout shift score is 0.75.&lt;/p&gt;
&lt;p&gt;The next example illustrates how adding content to an existing element affects
the layout shift score:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/layout-instability-api/layout-shift-2.png&quot; alt=&quot;Layout shift example with stable and unstable elements and viewportclipping&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here the &amp;quot;Click Me!&amp;quot; button is appended to the bottom of the gray box with
black text, which pushes the green box with white text down (and partially out
of the viewport).&lt;/p&gt;
&lt;p&gt;In this example, the gray box changes size, but its start position does not
change so it&#39;s not an unstable element. The start position of the green box,
however, does change, but since it&#39;s been moved partially out of the viewport,
the invisible area is not considered when calculating the layout shift score.
The union of the visible areas for the green box in both frames is the same as
the area of the green box in the first frame, which is 50%, and its layout
shift score is therefore 0.5 (as illustrated by the red, dotted rectangle).&lt;/p&gt;
&lt;p&gt;This last example illustrates multiple unstable elements:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/layout-instability-api/layout-shift-3.png&quot; alt=&quot;Layout shift example with multiple stable and unstableelements&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the first frame above we have the initial results of an API request for
animals, sorted in alphabetical order. In the second frame we have some more
results that get added to the sorted list.&lt;/p&gt;
&lt;p&gt;The first item in the list (&amp;quot;Cat&amp;quot;) does not change its start position between
frames, so it&#39;s stable. Similarly, the new items added to the list were not
previously in the DOM, so their start positions don&#39;t change either. But the
items labelled &amp;quot;Dog&amp;quot;, &amp;quot;Horse&amp;quot;, and &amp;quot;Zebra&amp;quot; all shift their start positions, so
they&#39;re unstable elements.&lt;/p&gt;
&lt;p&gt;Again, the red, dotted rectangles represent the union of these three unstable
elements&#39; before and after areas, which in this case represents around 38% of
the viewport&#39;s area (a layout shift score of 0.38).&lt;/p&gt;
&lt;p&gt;A key point that&#39;s hopefully clear from these examples is that layout shifts
only occur when &lt;em&gt;existing&lt;/em&gt; elements change their &lt;em&gt;start position&lt;/em&gt;. If a new
element is added to the DOM or an existing element changes size, it doesn&#39;t
count as a layout shift—&lt;strong&gt;as long as the change doesn&#39;t cause other visible
elements to change their start position&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;expected-vs-unexpected-layout-shifts&quot;&gt;Expected vs unexpected layout shifts &lt;a class=&quot;w-headline-link&quot; href=&quot;#expected-vs-unexpected-layout-shifts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Not all layout shifts are bad. In fact, many dynamic web applications will
frequently change the start position of elements on the page.&lt;/p&gt;
&lt;h4 id=&quot;user-initiated-layout-shifts&quot;&gt;User-initiated layout shifts &lt;a class=&quot;w-headline-link&quot; href=&quot;#user-initiated-layout-shifts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A layout shift is only bad if the user isn&#39;t expecting it. On the other hand,
layout shifts that occur in response to user interactions (e.g., clicking a
link, pressing a button, typing in a search box, etc.) are generally fine, as
long as the shift occurs close enough to the interaction that the relationship
is clear to the user.&lt;/p&gt;
&lt;p&gt;For example, if a user interaction triggers a network request that may take a
while to complete, it&#39;s best to create some space right away and show a loading
indicator to avoid an unpleasant layout shift when the request completes. If
the user doesn&#39;t realize something is loading, or doesn&#39;t have a sense of when
the resource will be ready, they may try to click something else while
waiting—something that could move out from under them.&lt;/p&gt;
&lt;h4 id=&quot;animations-and-transitions&quot;&gt;Animations and transitions &lt;a class=&quot;w-headline-link&quot; href=&quot;#animations-and-transitions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Animations and transitions, when done well, are a great way to update content
on the page without surprising the user. Content that shifts abruptly and
unexpectedly on the page almost always creates a bad user experience. But
content that moves gradually and naturally from one position to the next can
often help the user better understand what&#39;s going on, and guide them between
state changes.&lt;/p&gt;
&lt;p&gt;To ensure that effective uses of animations and transitions do not produce
negative layout shift scores in the Layout Instability API, elements whose
start position has changed by less than three pixels since the previous frame
are not considered unstable elements.&lt;/p&gt;
&lt;h3 id=&quot;a-cumulative-layout-shift-score&quot;&gt;A cumulative layout shift score &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-cumulative-layout-shift-score&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Layout shift scores are calculated per-frame, and they are reported regardless
of whether the shift was triggered as a result of user input.&lt;/p&gt;
&lt;p&gt;But users can experience layout instability throughout their entire browser
session, and as I mentioned above, not all layout shifts are perceived
negatively.&lt;/p&gt;
&lt;p&gt;The cumulative layout shift (CLS) score is determined by calculating the sum of
all unexpected layout shift scores from page load until the page&#39;s &lt;a href=&quot;https://developers.google.com/web/updates/2018/07/page-lifecycle-api&quot;&gt;lifecycle
state&lt;/a&gt;
changes to hidden. In the current Chrome implementation, &amp;quot;unexpected layout
shifts&amp;quot; are those that don&#39;t occur within 500 milliseconds of a discrete user
interaction (i.e., click, taps, and key presses, but not scrolls or mouse
moves)&lt;/p&gt;
&lt;p&gt;A page that has no unexpected layout shifts will have a cumulative layout shift
score of 0. Most typical content sites and web applications should strive for a
score of 0 to provide the best experience for their users.&lt;/p&gt;
&lt;p&gt;In addition, the cumulative layout shift score has been added as an
experimental metric in the &lt;a href=&quot;https://developers.google.com/web/tools/chrome-user-experience-report/&quot;&gt;Chrome User Experience
Report&lt;/a&gt;
(CrUX), a dataset that captures how real-world Chrome users experience popular
destinations on the web. With the addition of CLS as of the May 2019 release,
developers can get a better understanding of how users experience layout
instability on their sites, on their competitors&#39; sites, and on the web as a
whole.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; while most sites should strive for a CLS score of 0, it&#39;s
certainly possible that some sites employ layout shifts deliberately  (for
example, in multimedia presentations, progressive visualisations, slideshows,
etc.).&lt;/p&gt;
&lt;p&gt;This is perfectly fine. CLS scores are intended to help developers who may
not be aware of the problems caused by unexpected layout shifts; they&#39;re not
intended to suggest that sites that deliberately shift the layout are
necessarily problematic.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;how-to-use-the-layout-instability-api&quot;&gt;How to use the Layout Instability API &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-use-the-layout-instability-api&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Layout Instability API is available in Chrome 74+ with the experimental web
platform features flag enabled
(&lt;code&gt;chrome://flags/#enable-experimental-web-platform-features&lt;/code&gt;) or by through the
&lt;a href=&quot;https://developers.chrome.com/origintrials/#/view_trial/1215971899390033921&quot;&gt;Layout Instability Origin
Trial&lt;/a&gt;,
which will be available until Sept. 3, 2019.&lt;/p&gt;
&lt;p&gt;Similar to other performance APIs, Layout Instability can be observed via the
&lt;code&gt;PerformanceObserver&lt;/code&gt; interface, where you can subscribe to entries of type
&lt;code&gt;layout-shift&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following code logs all layout shift entries as they happen:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;layout-shift&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffered&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The
&lt;a href=&quot;https://w3c.github.io/performance-timeline/#dom-performanceobserverinit-buffered&quot;&gt;&lt;code&gt;buffered&lt;/code&gt;&lt;/a&gt;
flag in the above example (supported in Chrome 77+) gives you access to entries
that may have occurred prior to creating the &lt;code&gt;PerformanceObserver&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
The &lt;code&gt;entryType&lt;/code&gt; value for this API has changed a few times during
the experimentation period. In Chrome 76 it was &lt;code&gt;layoutShift&lt;/code&gt;, and in Chrome
74-75 it was &lt;code&gt;layoutJank&lt;/code&gt;. Developers implementing the stable API should only
need to observe the current &lt;code&gt;layout-shift&lt;/code&gt; value (as shown in the example
above), but developers who are part of the origin trial may need to observe
multiple entry types to cover their full user base. See &lt;a href=&quot;https://output.jsbin.com/zajamil/quiet&quot;&gt;this
demo&lt;/a&gt; for an example of code that
works in Chrome 74+.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you want to calculate the cumulative layout shift score for your pages and
track them in your analytics back end, you can declare a variable that stores
the current cumulative layout shift score, and then increment it any time a new
layout shift is detected. You&#39;ll typically want to record scores from the
initial page load until the page&#39;s lifecycle state changes to hidden:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Stores the current layout shift score for the page.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cumulativeLayoutShiftScore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Detects new layout shift occurrences and updates the&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// `cumulativeLayoutShiftScore` variable.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Only count layout shifts without recent user input.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hadRecentInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      cumulativeLayoutShiftScore &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;layout-shift&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; buffered&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Sends the final score to your analytics back end once&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// the page&#39;s lifecycle state becomes hidden.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visibilitychange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;visibilityState &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Force any pending records to be dispatched.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;takeRecords&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Send the final score to your analytics back end&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// (assumes `sendToAnalytics` is defined elsewhere).&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;sendToAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;cumulativeLayoutShiftScore&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-to-avoid-unexpected-layout-shifts&quot;&gt;How to avoid unexpected layout shifts &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-avoid-unexpected-layout-shifts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For most websites, you can avoid all unexpected layout shifts by sticking to a
few guiding principles:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;strong&gt;Always include size attributes on your images and video elements,
    or otherwise reserve the required space with something like
    &lt;a href=&quot;https://css-tricks.com/aspect-ratio-boxes/&quot;&gt;CSS aspect ratio
    boxes&lt;/a&gt;.&lt;/strong&gt;&lt;br&gt;
    This approach ensures that the browser can allocate the correct amount of
    space in the document while the image is loading. Note that you can also
    use the
    &lt;a href=&quot;https://github.com/w3c/webappsec-feature-policy/blob/master/policies/unsized-media.md&quot;&gt;
    unsized-media feature policy&lt;/a&gt; to force this behavior in browsers that
    support feature policies. And in the future you&#39;ll be able to use the
    &lt;a href=&quot;https://github.com/WICG/intrinsicsize-attribute&quot;&gt;
    &lt;code&gt;intrinsicSize&lt;/code&gt; attribute&lt;/a&gt; to more easily address this issue.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;Never insert content above existing content, except in response to
    a user interaction.&lt;/strong&gt;&lt;br&gt;
    This ensures any layout shifts that occur are expected.
  &lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;When layout shifts are necessary, use transitions or animation to
    provide context and continuity to the user.&lt;/strong&gt;&lt;br&gt;
    This creates continuity from state to state that&#39;s easier for users to
    follow.
  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;feedback-wanted&quot;&gt;Feedback wanted &lt;a class=&quot;w-headline-link&quot; href=&quot;#feedback-wanted&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Layout Instability API is being incubated in the WICG, and this is the time
feedback from the developer community is the most helpful—specifically
feedback on how well the API works on real websites and with the actual
development techniques used today.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are there any false positives (good experiences reported as unexpected layout
shifts)?&lt;/li&gt;
&lt;li&gt;Are there any false negatives (clear examples of layout instability not being
reported)?&lt;/li&gt;
&lt;li&gt;Are the scores meaningful and the data actionable?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can give feedback by opening up an issue on the &lt;a href=&quot;https://github.com/WICG/layout-instability&quot;&gt;Layout Instability Spec&#39;s
GitHub repo&lt;/a&gt;, or by contributing to
the discussion already happening there.&lt;/p&gt;
&lt;p&gt;Also, if your site is available in the CrUX dataset, you &lt;a href=&quot;https://console.cloud.google.com/bigquery?p=chrome-ux-report&amp;amp;d=all&amp;amp;t=201905&amp;amp;page=table&quot;&gt;can
query&lt;/a&gt;
to see how real Chrome users are experiencing the stability of your site&#39;s
layout, and you can compare these results to the results you&#39;re seeing when
testing your site locally. If the results you see in CrUX are unexpected, you
can sign up for the &lt;a href=&quot;https://developers.chrome.com/origintrials/#/view_trial/1215971899390033921&quot;&gt;Layout Instability Origin
Trial&lt;/a&gt;
and monitor your user&#39;s experience via the JavaScript API.&lt;/p&gt;
&lt;p&gt;As I mentioned in the introduction of this post. The first step toward properly
solving layout instability is to measure it and understand how often it occurs
for real users.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Making JavaScript and Google Search work together</title>
    <link href="https://web.dev/javascript-and-google-search-io-2019/"/>
    <updated>2019-06-04T17:00:00-07:00</updated>
    <id>https://web.dev/javascript-and-google-search-io-2019/</id>
    <content type="html">&lt;p&gt;Great things are happening with Google Search, and we were excited to share them at Google I/O 2019!&lt;/p&gt;
&lt;div style=&quot;width:100%; padding-top: 56.25%; position: relative;&quot;&gt;
&lt;iframe style=&quot;width:100%; height: 100%;position: absolute; top: 50%; left:
50%; transform: translate(-50%,-50%);&quot; src=&quot;https://www.youtube.com/embed/ufcijo46LCU&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;In this post we&#39;ll focus on best practices for making JavaScript web apps discoverable in Google Search, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The new evergreen Googlebot&lt;/li&gt;
&lt;li&gt;Googlebot&#39;s pipeline for crawling, rendering and indexing&lt;/li&gt;
&lt;li&gt;Feature detection and error handling&lt;/li&gt;
&lt;li&gt;Rendering strategies&lt;/li&gt;
&lt;li&gt;Testing tools for your website in Google Search&lt;/li&gt;
&lt;li&gt;Common challenges and possible solutions&lt;/li&gt;
&lt;li&gt;Best practices for SEO in JavaScript web apps&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;meet-the-evergreen-googlebot&quot;&gt;Meet the evergreen Googlebot &lt;a class=&quot;w-headline-link&quot; href=&quot;#meet-the-evergreen-googlebot&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This year we announced the much-awaited &lt;a href=&quot;https://webmasters.googleblog.com/2019/05/the-new-evergreen-googlebot.html&quot;&gt;new evergreen Googlebot&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/javascript-and-google-search-io-2019/evergreen-googlebot.png&quot; alt=&quot;Googlebot holding the Chrome logo&quot; style=&quot;max-width: 400px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Googlebot is now running a modern Chromium rendering engine.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Googlebot now uses a modern Chromium engine to render websites for Google Search. On top of that, we will test newer versions of Chromium to &lt;em&gt;keep&lt;/em&gt; Googlebot updated, usually within a few weeks of each stable Chrome release. This announcement is big news for web developers and SEOs because it marks the arrival of &lt;a href=&quot;https://caniuse.com/#compare=chrome+41,chrome+74&quot;&gt;1000+ new features&lt;/a&gt;—such as ES6+, &lt;code&gt;IntersectionObserver&lt;/code&gt;, and Web Components v1—in Googlebot.&lt;/p&gt;
&lt;h2 id=&quot;learn-how-googlebot-works&quot;&gt;Learn how Googlebot works &lt;a class=&quot;w-headline-link&quot; href=&quot;#learn-how-googlebot-works&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Googlebot is a pipeline with several components. Let&#39;s take a look to understand how Googlebot indexes pages for Google Search.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center w-figure--fullbleed&quot;&gt;
  &lt;img src=&quot;https://web.dev/javascript-and-google-search-io-2019/googlebot-process.png&quot; alt=&quot;A diagram showing a URL moving from a crawling queue to a processing step that extracts linked URLs and adds them to the crawling queue, a rendering queue that feeds into a renderer which produces HTML. The processor uses this HTML to extract linked URLs again and index the content.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    Googlebot&#39;s pipeline for crawling, rendering, and indexing a page.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The process works like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Googlebot queues URLs for crawling.&lt;/li&gt;
&lt;li&gt;It then fetches the URLs with an HTTP request based on the &lt;a href=&quot;https://webmasters.googleblog.com/2017/01/what-crawl-budget-means-for-googlebot.html&quot;&gt;crawl budget&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Googlebot scans the HTML for links and queues the discovered links for crawling.&lt;/li&gt;
&lt;li&gt;Googlebot then queues the page for rendering.&lt;/li&gt;
&lt;li&gt;As soon as possible, a headless Chromium instance renders the page, which includes JavaScript execution.&lt;/li&gt;
&lt;li&gt;Googlebot uses the rendered HTML to index the page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your technical setup can influence the process of crawling, rendering, and indexing. For example, slow response times or server errors can impact the &lt;a href=&quot;https://webmasters.googleblog.com/2017/01/what-crawl-budget-means-for-googlebot.html&quot;&gt;crawl budget&lt;/a&gt;. Another example would be requiring JavaScript to render the links can lead to a slower discovery of these links.&lt;/p&gt;
&lt;h2 id=&quot;use-feature-detection-and-handle-errors&quot;&gt;Use feature detection and handle errors &lt;a class=&quot;w-headline-link&quot; href=&quot;#use-feature-detection-and-handle-errors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The evergreen Googlebot has lots of new features, but some features are still not supported. Relying on unsupported features or not handling errors properly can mean Googlebot can&#39;t render or index your content.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at an example:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;     navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geolocation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;       &lt;span class=&quot;token function&quot;&gt;loadLocalContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This page might not show any content in some cases because the code doesn&#39;t handle when the user declines the permission or when getCurrentPosition call returns an error. Googlebot declines permission requests like this by default.&lt;/p&gt;
&lt;p&gt;This is a better solution:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;     &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geolocation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;       &lt;span class=&quot;token comment&quot;&gt;// this browser supports the Geolocation API, request location!&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;       navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geolocation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;           &lt;span class=&quot;token comment&quot;&gt;// we successfully got the location, show local content&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;           &lt;span class=&quot;token function&quot;&gt;loadLocalContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;           &lt;span class=&quot;token comment&quot;&gt;// we failed to get the location, show fallback content&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;           &lt;span class=&quot;token function&quot;&gt;loadGlobalContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;       &lt;span class=&quot;token comment&quot;&gt;// this browser does not support the Geolocation API, show fallback content&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;       &lt;span class=&quot;token function&quot;&gt;loadGlobalContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have problems with getting your JavaScript site indexed, &lt;a href=&quot;https://developers.google.com/search/docs/guides/fix-search-javascript&quot;&gt;walk through our troubleshooting guide&lt;/a&gt; to find solutions.&lt;/p&gt;
&lt;h2 id=&quot;choose-the-right-rendering-strategy-for-your-web-app&quot;&gt;Choose the right rendering strategy for your web app &lt;a class=&quot;w-headline-link&quot; href=&quot;#choose-the-right-rendering-strategy-for-your-web-app&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default rendering strategy for single-page apps today is client-side rendering. The HTML loads the JavaScript, which then generates the content in the browser as it executes.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at a web app that shows a collection of cat images and uses JavaScript to render entirely in the browser.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img src=&quot;https://web.dev/javascript-and-google-search-io-2019/spa-kittens.png&quot; alt=&quot;A code box showing HTML that loads some scripts. A screenshot of a web page on mobile that shows placeholder images while loading the actual content.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    The rendering strategy influences the performance and robustness of your web apps.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you&#39;re free to choose your rendering strategy, consider server-side rendering or pre-rendering. They execute JavaScript on the server to generate the initial HTML content, which can improve performance for both users and crawlers. These strategies allow the browser to start rendering HTML as it arrives over the network, making the page load faster.  The &lt;a href=&quot;https://www.youtube.com/watch?v=k-A2VfuUROg&quot;&gt;rendering session at I/O&lt;/a&gt;  or &lt;a href=&quot;https://developers.google.com/web/updates/2019/02/rendering-on-the-web&quot;&gt;the blog post about rendering on the web&lt;/a&gt; shows how server-side rendering and hydration can improve the performance and user experience of web apps and provides more code examples for these strategies.&lt;/p&gt;
&lt;p&gt;If you&#39;re looking for a workaround to help crawlers that don&#39;t execute JavaScript—or if you can&#39;t make changes to your frontend codebase—consider &lt;a href=&quot;https://developers.google.com/search/docs/guides/dynamic-rendering&quot;&gt;dynamic rendering&lt;/a&gt;, which you can try out in &lt;a href=&quot;https://codelabs.developers.google.com/codelabs/dynamic-rendering&quot;&gt;this codelab&lt;/a&gt;. Note, though, that you won&#39;t get the user experience or performance benefits that you would with server-side rendering or pre-rendering because dynamic rendering only serves static HTML to crawlers. That makes it a stop-gap rather than a long-term strategy.&lt;/p&gt;
&lt;h2 id=&quot;test-your-pages&quot;&gt;Test your pages &lt;a class=&quot;w-headline-link&quot; href=&quot;#test-your-pages&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While most pages generally work fine with Googlebot, we recommend testing your pages regularly to make sure your content is available to Googlebot and there are no problems. There are several great tools to help you do that.&lt;/p&gt;
&lt;p&gt;The easiest way to do a quick check of a page is the &lt;a href=&quot;https://g.co/mobilefriendly&quot;&gt;Mobile-Friendly Test&lt;/a&gt;. Besides showing you issues with mobile-friendliness, it also gives you a screenshot of the above-the-fold content and the rendered HTML as Googlebot sees it.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/javascript-and-google-search-io-2019/mobile-friendly-test-rendered-html.png&quot; alt=&quot;The mobile-friendly test shows the rendered HTML Googlebot sees after rendering the page&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    The mobile-friendly test shows you the rendered HTML Googlebot uses.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can also find out if there are resource loading issues or JavaScript errors.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/javascript-and-google-search-io-2019/mobile-friendly-test-js-error.png&quot; alt=&quot;The Mobile-Friendly Test shows JavaScript errors and a stack trace.&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;It&#39;s recommended to verify your domain in &lt;a href=&quot;https://g.co/searchconsole&quot;&gt;Google Search Console&lt;/a&gt; so you can use the URL inspection tool to find out more about the crawling and indexing state of a URL, receive messages when Search Console detects issues and find out more details of how your site performs in Google Search.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/javascript-and-google-search-io-2019/search-console-url-inspection-tool.png&quot; alt=&quot;The URL inspection tool showing a page that is indexed with information on discovery, crawling and indexing for one URL&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    The URL Inspection Tool in Search Console shows the status of a page in crawling, rendering, and indexing.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For general SEO tips and guidance, you can use the SEO audits in Lighthouse. To integrate SEO audits into your testing suite, use the &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-cli&quot;&gt;Lighthouse CLI&lt;/a&gt; or the &lt;a href=&quot;https://github.com/GoogleChromeLabs/lighthousebot&quot;&gt;Lighthouse CI bot&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/javascript-and-google-search-io-2019/lighthouse-seo-audit-report.png&quot; alt=&quot;A lighthouse SEO report with a score of 44 and warnings about mobile-friendliness as well as warnings about content best practices&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Lighthouse shows general SEO recommendations for a page.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These tools help you identify, debug, and fix issues with pages in Google Search and should be part of your development routine.&lt;/p&gt;
&lt;h2 id=&quot;stay-up-to-date-and-get-in-touch&quot;&gt;Stay up to date and get in touch &lt;a class=&quot;w-headline-link&quot; href=&quot;#stay-up-to-date-and-get-in-touch&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To stay up to date with announcements and changes to Google Search, keep an eye on our &lt;a href=&quot;https://webmasters.googleblog.com/&quot;&gt;Webmasters Blog&lt;/a&gt;, the &lt;a href=&quot;https://youtube.com/GoogleWebmasterHelp&quot;&gt;Google Webmasters Youtube channel&lt;/a&gt;, and our &lt;a href=&quot;https://twitter.com/googlewmc&quot;&gt;Twitter account&lt;/a&gt;.
Also check out our &lt;a href=&quot;http://developers.google.com/search/docs/guides/&quot;&gt;developer guide to Google Search&lt;/a&gt; and our &lt;a href=&quot;https://www.youtube.com/watch?v=LXF8bM4g-J4&amp;amp;list=PLKoqnv2vTMUPOalM1zuWDP9OQl851WMM9&quot;&gt;JavaScript SEO video series&lt;/a&gt; to learn more about SEO and JavaScript.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Service worker mindset</title>
    <link href="https://web.dev/service-worker-mindset/"/>
    <updated>2019-06-03T17:00:00-07:00</updated>
    <id>https://web.dev/service-worker-mindset/</id>
    <content type="html">&lt;p&gt;Service workers are powerful and absolutely worth mastering. They let you deliver an entirely new level of experience to your users. Your site can load &lt;em&gt;instantly&lt;/em&gt;. It can work &lt;em&gt;offline&lt;/em&gt;. It can be installed as a native app and feel every bit as polished—but with the reach and freedom of the web.&lt;/p&gt;
&lt;p&gt;But service workers are unlike anything most of us web devs are used to. They come with a steep learning curve and a handful of snags you&#39;ve got to watch out for.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/ChromiumDev&quot;&gt;Google Developers&lt;/a&gt; and I recently collaborated on a project—&lt;a href=&quot;https://serviceworkies.com/&quot;&gt;Service Workies&lt;/a&gt;—a free game for mastering service workers. While building it and working with the complex ins and outs of service workers, I ran into a few snags. What helped me the most was coming up with a handful of depictive metaphors. In this post we&#39;ll explore these mental models and wrap our brains around the paradoxical traits that make service workers both tricky and awesome.&lt;/p&gt;
&lt;h2 id=&quot;the-same-but-different&quot;&gt;The same, but different &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-same-but-different&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While coding your service worker, many things will feel familiar. You get to use your favorite new JavaScript language features. You listen to lifecycle events just like with UI events. You manage control flow with promises like you&#39;re used to.&lt;/p&gt;
&lt;p&gt;But other service worker behavior causes you to scratch your head in confusion. Especially when you refresh the page and don&#39;t see your code changes applied.&lt;/p&gt;
&lt;h3 id=&quot;a-new-layer&quot;&gt;A new layer &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-new-layer&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Normally when building a site you have just two layers to think about: the client and the server. The service worker is a brand new layer that sits in the middle.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/service-worker-mindset/middle-layer.jpg&quot; alt=&quot;A service worker acts as a middle layer between the client and the server&quot;&gt;&lt;/p&gt;
&lt;p&gt;Think of your service worker as a sort of &lt;em&gt;browser extension&lt;/em&gt;—one that your site can install in your user&#39;s browser. Once installed, the service worker &lt;em&gt;extends&lt;/em&gt; the browser for your site with a powerful middle layer. This service worker layer can intercept and handle all of the requests your site makes.&lt;/p&gt;
&lt;p&gt;The service worker layer has its &lt;strong&gt;own lifecycle&lt;/strong&gt; independent of the browser tab. A simple page refresh isn&#39;t enough to update a service worker—just like you wouldn&#39;t expect a page refresh to update code deployed on a server. Each layer has its own unique rules for updating.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://serviceworkies.com/&quot;&gt;Service Workies&lt;/a&gt; game we cover the many details of the service worker lifecycle and give you a ton of practice working with it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/service-worker-mindset/update-lifecycle.gif&quot; alt=&quot;a new service worker replacing an old one&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Think of your service worker as a new middle layer with its own lifecycle and methods for updating.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;powerful-but-limited&quot;&gt;Powerful, but limited &lt;a class=&quot;w-headline-link&quot; href=&quot;#powerful-but-limited&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Having a service worker on your site gives you incredible benefits. Your site can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;work flawlessly even when the user is offline&lt;/li&gt;
&lt;li&gt;gain massive performance improvements through &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Cache&quot;&gt;caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;use &lt;a href=&quot;https://developers.google.com/web/fundamentals/push-notifications/&quot;&gt;push notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;be installed as a &lt;a href=&quot;https://developers.google.com/web/progressive-web-apps/&quot;&gt;PWA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With as much as service workers can do, they are limited by design. They can&#39;t do anything synchronous or in the same thread as your site. So that means no access to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;localStorage&lt;/li&gt;
&lt;li&gt;the DOM&lt;/li&gt;
&lt;li&gt;the window&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The good news is there are a handful of ways your page can communicate with its service worker, including direct &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage&quot;&gt;&lt;code&gt;postMessage&lt;/code&gt;&lt;/a&gt;, one-to-one &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel&quot;&gt;Message Channels&lt;/a&gt; and one-to-many &lt;a href=&quot;https://developers.google.com/web/updates/2016/09/broadcastchannel&quot;&gt;Broadcast Channels&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Think of your service worker as something that lives outside of your page. You can talk to it, but it can&#39;t access your page directly.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;long-lived-but-short-lived&quot;&gt;Long-lived, but short-lived &lt;a class=&quot;w-headline-link&quot; href=&quot;#long-lived-but-short-lived&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An active service worker goes on living even after a user leaves your site or closes the tab. The browser keeps this service worker around so that it will be ready the next time the user comes back to your site. Before the very first request is made, the service worker gets a chance to intercept it and take control of the page. This is what allows a site to work offline—the service worker can serve a cached version of the page itself, even if the user has no connection to the internet.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://serviceworkies.com/&quot;&gt;Service Workies&lt;/a&gt; we visualize this concept with Kolohe (a friendly service worker) intercepting and handling requests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/service-worker-mindset/intercept.gif&quot; alt=&quot;service worker intercepting http requests&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;stopped&quot;&gt;Stopped &lt;a class=&quot;w-headline-link&quot; href=&quot;#stopped&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Despite service workers appearing to be immortal, they can be &lt;strong&gt;stopped&lt;/strong&gt; at almost any time. The browser doesn&#39;t want to waste resources on a service worker that isn&#39;t currently doing anything. Getting stopped isn&#39;t the same thing as getting &lt;em&gt;terminated&lt;/em&gt;—the service worker remains installed and activated. It&#39;s just put to sleep. The next time it&#39;s needed (e.g., to handle a request), the browser wakes it back up.&lt;/p&gt;
&lt;h3 id=&quot;waituntil&quot;&gt;waitUntil &lt;a class=&quot;w-headline-link&quot; href=&quot;#waituntil&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Because of the constant possibility of getting put to sleep, your service worker needs a way to let the browser know when it&#39;s doing something important and doesn&#39;t feel like taking a nap. This is where &lt;code&gt;event.waitUntil()&lt;/code&gt; comes into play. This method extends the lifecycle it&#39;s used in, keeping it both from being stopped and from moving on to the next phase of its lifecycle until we&#39;re ready. This gives us time to setup caches, fetch resources from the network, etc.&lt;/p&gt;
&lt;p&gt;This example tells the browser that our service worker isn&#39;t done installing until the &lt;code&gt;assets&lt;/code&gt; cache has been created and populated with the picture of a sword:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;assets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/weapons/sword/blade.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;watch-out-for-global-state&quot;&gt;Watch out for global state &lt;a class=&quot;w-headline-link&quot; href=&quot;#watch-out-for-global-state&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When this start/stop happens the service worker&#39;s global scope is reset. So be careful not to use any global state in your service worker or you&#39;ll be sad the next time it wakes back up and has different state from what it was expecting.&lt;/p&gt;
&lt;p&gt;Consider this example that uses a global state:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; favoriteNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; hasHandledARequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fetch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;favoriteNumber&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasHandledARequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  hasHandledARequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On each request this service worker will log a number—let&#39;s say &lt;code&gt;0.13981866382421893&lt;/code&gt;. The &lt;code&gt;hasHandledARequest&lt;/code&gt; variable also changes to &lt;code&gt;true&lt;/code&gt;. Now the service worker sits idle for a bit, so the browser stops it. The next time there&#39;s a request, the service worker is needed again, so the browser wakes it up. Its script is &lt;strong&gt;evaluated again&lt;/strong&gt;. Now &lt;code&gt;hasHandledARequest&lt;/code&gt; is reset to &lt;code&gt;false&lt;/code&gt;, and &lt;code&gt;favoriteNumber&lt;/code&gt; is something completely different—&lt;code&gt;0.5907281835659033&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can&#39;t rely on stored state in a service worker. Also, creating instances of things like Message Channels can cause bugs: you&#39;ll get a brand new instance every time the service worker stops/starts.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt;
This snag is especially important to keep in mind while working on your service worker code because when Chrome DevTools is open, the start/stop behavior is disabled. You may not even see bugs caused by relying on global state until they&#39;ve shipped to your users.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;a href=&quot;https://gedd.ski/post/service-workies-chapter3/&quot;&gt;Service Workies chapter 3&lt;/a&gt; we visualize our stopped service worker as losing all color while he hangs out waiting to be woken up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/service-worker-mindset/kolohe-stopped.jpg&quot; alt=&quot;visualization of a stopped service worker&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Think of your service worker as a &lt;a href=&quot;https://www.akc.org/dog-breeds/whippet/&quot;&gt;whippet&lt;/a&gt; dog. He&#39;s fast, loyal and awesome. He&#39;ll stick around by your side no matter what. But mostly he just wants to sleep. All the time. You&#39;ve got to let him know when you want him to stay awake. Good boy!&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;together-but-separate&quot;&gt;Together, but separate &lt;a class=&quot;w-headline-link&quot; href=&quot;#together-but-separate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your page can only be &lt;em&gt;controlled&lt;/em&gt; by one service worker at a time. But it can have two service workers &lt;em&gt;installed&lt;/em&gt; at once. When you make a change to your service worker code and refresh the page, you aren&#39;t actually editing your service worker at all. Service workers are &lt;em&gt;immutable&lt;/em&gt;. You&#39;re instead making a brand new one. This new service worker (let&#39;s call it SW2) will &lt;em&gt;install&lt;/em&gt; but it won&#39;t &lt;em&gt;activate&lt;/em&gt; yet. It has to &lt;em&gt;wait&lt;/em&gt; for the current service worker (SW1) to terminate (when your user leaves your site).&lt;/p&gt;
&lt;h3 id=&quot;messing-with-another-service-worker&#39;s-caches&quot;&gt;Messing with another service worker&#39;s caches &lt;a class=&quot;w-headline-link&quot; href=&quot;#messing-with-another-service-worker&#39;s-caches&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While installing, SW2 can get things setup—usually creating and populating caches. But heads up: this new service worker has access to everything that the current service worker has access to. If you&#39;re not careful, your new waiting service worker can really mess things up for your current service worker. Some examples that could cause you trouble:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SW2 could delete a cache that SW1 is actively using.&lt;/li&gt;
&lt;li&gt;SW2 could edit the contents of a cache that SW1 is using, causing SW1 to respond with assets that the page isn&#39;t expecting.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;skip-skipwaiting&quot;&gt;Skip skipWaiting &lt;a class=&quot;w-headline-link&quot; href=&quot;#skip-skipwaiting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A service worker can also use the risky &lt;code&gt;skipWaiting()&lt;/code&gt; method to take control of the page as soon as it&#39;s done installing. This is generally a bad idea unless you&#39;re intentionally trying to replace a buggy service worker. The new service worker might be using updated resources that the current page isn&#39;t expecting, leading to errors and bugs.&lt;/p&gt;
&lt;h3 id=&quot;start-clean&quot;&gt;Start clean &lt;a class=&quot;w-headline-link&quot; href=&quot;#start-clean&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The way to prevent your service workers from clobbering each other is to make sure they use different caches. The easiest way to accomplish that is to version the cache names they use.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; assetCacheName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;assets-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;version&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;assetCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// confidently do stuff with your very own cache&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you deploy a new service worker, you&#39;ll bump the &lt;code&gt;version&lt;/code&gt; so that it does what it needs with an entirely separate cache from the previous service worker.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/service-worker-mindset/cache.jpg&quot; alt=&quot;visualization of a cache&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;end-clean&quot;&gt;End clean &lt;a class=&quot;w-headline-link&quot; href=&quot;#end-clean&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once your service worker reaches the &lt;code&gt;activated&lt;/code&gt; state, you know it has taken over, and the previous service worker is redundant (i.e., no longer needed). At this point it&#39;s important to clean up after the old service worker. Not only does it respect your users&#39; cache storage limits, but it can also prevent unintentional bugs.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/match&quot;&gt;&lt;code&gt;caches.match()&lt;/code&gt;&lt;/a&gt; method is an often-used shortcut for retrieving an item from &lt;em&gt;any&lt;/em&gt; cache where there&#39;s a match. But it iterates through the caches in the order they were created. So let&#39;s say you&#39;ve got two versions of a script file &lt;code&gt;app.js&lt;/code&gt; hanging out in two different caches—&lt;code&gt;assets-1&lt;/code&gt; and &lt;code&gt;assets-2&lt;/code&gt;. Your page is expecting the newer script that&#39;s stored in &lt;code&gt;assets-2&lt;/code&gt;. But if you haven&#39;t deleted the old cache, &lt;code&gt;caches.match(&#39;app.js&#39;)&lt;/code&gt; is going to return the old one from &lt;code&gt;assets-1&lt;/code&gt; and most likely break your site.&lt;/p&gt;
&lt;p&gt;All it takes to clean up after previous service workers is to delete any cache that the new service worker doesn&#39;t need:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; assetCacheName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;assets-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;version&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;activate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cacheNames&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        cacheNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cacheName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheName &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; assetCacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preventing your service workers from clobbering each other takes a bit of work and disciple but is worth the trouble.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Think of the combination of your service worker and your site as an &lt;a href=&quot;https://web.dev/installable&quot;&gt;installable&lt;/a&gt; app. Each version should work. Each version should be separate from the others. Imagine how buggy a game would be if the developer accidentally released a patch that used new game logic but outdated assets. You&#39;d rage on the forums so fast! Keep your app versions tidy &amp;amp; clean.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;service-worker-mindset&quot;&gt;Service worker mindset &lt;a class=&quot;w-headline-link&quot; href=&quot;#service-worker-mindset&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Getting into the right mindset while thinking about service workers will help you build yours with confidence. Once you get the hang of them you&#39;ll be able to create incredible experiences for your users.&lt;/p&gt;
&lt;p&gt;If you want to master all this by &lt;a href=&quot;https://gedd.ski/post/mastery-through-play/&quot;&gt;playing a game&lt;/a&gt;, then you&#39;re in luck! Go play &lt;a href=&quot;https://serviceworkies.com/&quot;&gt;Service Workies&lt;/a&gt; where you&#39;ll learn the ways of the service worker in order to slay the offline beasts.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://web.dev/service-worker-mindset/spider.gif&quot; alt=&quot;preview of the Service Workies game&quot;&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>What should you measure to improve performance?</title>
    <link href="https://web.dev/what-should-you-measure-to-improve-performance/"/>
    <updated>2019-05-30T17:00:00-07:00</updated>
    <id>https://web.dev/what-should-you-measure-to-improve-performance/</id>
    <content type="html">&lt;p&gt;The different steps of a purchase funnel are prone to performance issues in
different ways, and therefore need different measurement and optimizations:&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/what-should-you-measure-to-improve-performance/funnel.png&quot; alt=&quot;A conversion funnel going from discover to engage to convert to re-engage.&quot; style=&quot;max-width: 600px; width: 100%;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    A conversion funnel.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this guide we&#39;ll address how the different steps can be measured. For this we
recommend you to look at lab as well as field data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lab data&lt;/strong&gt; is gathered by running tests locally, for example by using
&lt;a href=&quot;https://web.dev/fast/discover-performance-opportunities-with-lighthouse&quot;&gt;Lighthouse&lt;/a&gt;
and other tools.  This can make it possible  to compare website performance over
time and with competitors through a controlled, stable environment, but it might
not be representative of the performance users experience in real life.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Field data&lt;/strong&gt; is gathered via analytics from real users, and is therefore
representative of their experience. However, it can&#39;t easily be compared over
time or with competitors. Network connections and smartphone hardware evolve
over time, and different target audiences might have different devices, thereby
making comparisons with field data tough. See also &lt;em&gt;&lt;a href=&quot;https://web.dev/fast#measure-performance-in-the-field&quot;&gt;Measure Performance In The Field&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To get a complete picture, both data sources are needed. The following sections
show how to gather lab and field data for the different relevant performance
marks across the funnel.&lt;/p&gt;
&lt;h2 id=&quot;discovery&quot;&gt;Discovery &lt;a class=&quot;w-headline-link&quot; href=&quot;#discovery&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Optimizing for discovery means optimizing for the first load, which is what new
users  will get, but also search and social
&lt;a href=&quot;https://developers.google.com/search/docs/guides/rendering&quot;&gt;crawlers&lt;/a&gt;.
Lab data for a first load can be easily acquired through
&lt;a href=&quot;https://web.dev/fast/discover-performance-opportunities-with-lighthouse&quot;&gt;Lighthouse&lt;/a&gt;,
while field data (at least for Chrome) is readily available through &lt;a href=&quot;https://web.dev/fast/chrome-ux-report&quot;&gt;Chrome UX
reports&lt;/a&gt;. A convenient combination of
both can be found in &lt;a href=&quot;https://developers.google.com/speed/pagespeed/insights/&quot;&gt;PageSpeed
Insights&lt;/a&gt;. You should
also track relevant metrics from the field yourself:
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#measuring_these_metrics_on_real_users_devices&quot;&gt;Measuring these metrics on real users&#39; devices&lt;/a&gt;
provides a good overview.&lt;/p&gt;
&lt;p&gt;From a user perspective the most important metrics are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/first-contentful-paint/&quot;&gt;First Contentful Paint&lt;/a&gt;
(FCP)&lt;/strong&gt;: The time the user stares at a blank screen. This is
when most users bounce, as they don&#39;t see progress.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/first-meaningful-paint/&quot;&gt;First Meaningful Paint&lt;/a&gt;
(FMP):&lt;/strong&gt; When the user begins to see the main content they came for. This
is often the hero image, but for a landing page it may even be a call to
action such as a &lt;strong&gt;Buy&lt;/strong&gt; button, since the user may have arrived with a clear
intent (for example, through a targeted ad campaign).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developers.google.com/web/updates/2018/05/first-input-delay&quot;&gt;First Input Delay&lt;/a&gt;
(FID):&lt;/strong&gt; The time the website needs to react to the user&#39;s first input.
Excessive JavaScript and other asset loading problems can block this,
leading to failed taps or clicks, erroneous inputs and page abandonment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are more metrics you can look at, but these are a good baseline. Additionally also make sure to capture bounce rates, conversions and user engagement so that you can set these in relation.&lt;/p&gt;
&lt;h2 id=&quot;engagement-and-conversion&quot;&gt;Engagement And Conversion &lt;a class=&quot;w-headline-link&quot; href=&quot;#engagement-and-conversion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After the first load of a landing page, a user will proceed through your site,
hopefully towards a successful conversion.&lt;/p&gt;
&lt;p&gt;In this phase it is important to have fast and responsive navigations and
interactions. Unfortunately it is not trivial to measure the complete flow of
navigation and interaction events in the field, as every user takes different
paths through the page. We therefore recommend measuring the time needed towards
conversion or conversion subgoals (&amp;quot;&lt;em&gt;Time-to-Action&lt;/em&gt;&amp;quot;) by scripting and
measuring the flow in a lab test, to compare performance over time or with
competitors.&lt;/p&gt;
&lt;p&gt;There are two convenient ways of doing this:&lt;/p&gt;
&lt;h3 id=&quot;webpagetest&quot;&gt;WebPageTest &lt;a class=&quot;w-headline-link&quot; href=&quot;#webpagetest&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;WebPageTest&lt;/a&gt; offers a very flexible &lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting&quot;&gt;scripting
solution&lt;/a&gt;.
The basic idea is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tell WebPageTest to navigate through the pages of the flow with the
&lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-navigate&quot;&gt;&lt;code&gt;navigate&lt;/code&gt;&lt;/a&gt;
command.&lt;/li&gt;
&lt;li&gt;If needed script the clicking of buttons via
&lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-clickAndWait&quot;&gt;&lt;code&gt;clickAndWait&lt;/code&gt;&lt;/a&gt;
commands and fill text fields via
&lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-setValue&quot;&gt;&lt;code&gt;setValue&lt;/code&gt;&lt;/a&gt;.
For testing of Single Page Applications use &lt;code&gt;clickAndWait&lt;/code&gt; rather than
&lt;code&gt;navigate&lt;/code&gt; commands for all steps after the first, as &lt;code&gt;navigate&lt;/code&gt; will do a
full load instead of the lightweight virtual page load.&lt;/li&gt;
&lt;li&gt;Make sure to combine the different steps of the flow in the analysis via
&lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-combineSteps&quot;&gt;&lt;code&gt;combineSteps&lt;/code&gt;&lt;/a&gt;
to produce a single overall result report for the complete flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Such a script could look like this:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;combineSteps&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;navigate	https://www.store.google.com/landingpage&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;navigate	https://www.store.google.com/productpage&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;clickAndWait	innerText=Buy Now&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;navigate	https://www.store.google.com/basket&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;navigate	https://www.store.google.com/checkout&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a script like this in place you can easily measure and compare performance
over time. This can even be automated through the
&lt;a href=&quot;https://sites.google.com/a/webpagetest.org/docs/advanced-features/webpagetest-restful-apis&quot;&gt;WebPageTest API&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;puppeteer&quot;&gt;Puppeteer &lt;a class=&quot;w-headline-link&quot; href=&quot;#puppeteer&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another great option to script testing is via headless Chrome, which can be
controlled through the Node API
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer&quot;&gt;Puppeteer&lt;/a&gt;. The general idea is to
start the browser through Puppeteer, navigate to the landing page through the
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options&quot;&gt;goto&lt;/a&gt;
function,
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageevaluatepagefunction-args&quot;&gt;inject Javascript&lt;/a&gt;
to fill fields or
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageclickselector-options&quot;&gt;click&lt;/a&gt;
buttons and proceed through the funnel through further
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options&quot;&gt;goto&lt;/a&gt;
calls as needed.&lt;/p&gt;
&lt;p&gt;As a metric the duration of the flow can be measured directly,
but you could also sum up the FCP, FMP or TTI values of the individual loads of
the flow.
&lt;a href=&quot;https://michaljanaszek.com/blog/test-website-performance-with-puppeteer&quot;&gt;Test website performance with Puppeteer&lt;/a&gt;
provides an overview of how to get performance metrics via Puppeteer. A very
simplified example Node script could look like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; puppeteer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;puppeteer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; puppeteer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/landingpage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/productpage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// click the buy button, which triggers overlay basket&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#buy_btn&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// wait until basket overlay is shown&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#close_btn&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/basket&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/checkout&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Flow took &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; seconds&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script can easily be automated, and even be made part of the build process
or perf budgets, and be monitored regularly.&lt;/p&gt;
&lt;h2 id=&quot;re-engagement&quot;&gt;Re-Engagement &lt;a class=&quot;w-headline-link&quot; href=&quot;#re-engagement&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Users will return to your site in different time intervals. Depending on time
passed, the browser may have less of the website&#39;s resources cached, needing
more network requests. This makes it difficult to estimate performance
differences across repeat visits in lab tests. It is still recommended to keep
an eye on this though, and a great lab test tool for repeat visits is
&lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;WebPageTest&lt;/a&gt;, which has a dedicated option for a
direct repeat visit:&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/what-should-you-measure-to-improve-performance/webpagetest_repeat.png&quot; alt=&quot;The WebPageTest homepage form for auditing a site. The repeat view option is highlighted.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--center&quot;&gt;
    Webpagetest offers options to test first load and repeat load as well
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To get a better feeling for repeat visit performance in the field use your
analytics package of choice to segment your performance metrics by user type.
Here is an example of such a report in Google Analytics:&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/what-should-you-measure-to-improve-performance/ga_speed_repeat.png&quot; alt=&quot;A Google Analytics dashboard shows a number of fields being added to a custom report.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--center&quot;&gt;
    A Google Analytics custom report can be used to report speed metrics for new and returning users.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A report like this will give you page load times for new and returning users as well.&lt;/p&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap &lt;a class=&quot;w-headline-link&quot; href=&quot;#recap&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide showed you how to measure first load, flow and repeat load via field and lab tests. Make sure to optimize the different steps of the funnel accordingly to maximize discovery (first load), engagement (navigations and flow) and re-engagement (repeat load).&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Image policies for fast load times and more</title>
    <link href="https://web.dev/image-policies/"/>
    <updated>2019-05-30T17:00:00-07:00</updated>
    <id>https://web.dev/image-policies/</id>
    <content type="html">&lt;p&gt;Images often take up a significant amount of visual space and make up the
majority of the downloaded bytes on a website. Optimizing images can improve
loading performance and reduce network traffic.&lt;/p&gt;
&lt;p&gt;Surprisingly, more than half of the sites on the web are serving poorly
compressed or unnecessarily large images. This leaves a lot of room for
performance improvements simply by optimizing the images.&lt;/p&gt;
&lt;p&gt;You may ask, how do I know if my images are optimized and how should I optimize
them? We are experimenting with a new set of feature policies for image
optimization: &lt;code&gt;oversized-images&lt;/code&gt;, &lt;code&gt;unoptimized-lossy-images&lt;/code&gt;,
&lt;code&gt;unoptimized-lossless-images&lt;/code&gt;, and&lt;code&gt;unoptimized-lossless-images-strict&lt;/code&gt;.
All are now available for &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md&quot;&gt;origin
trials&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;optimized-image-policies&quot;&gt;Optimized image policies &lt;a class=&quot;w-headline-link&quot; href=&quot;#optimized-image-policies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Feature policy is introducing a new set restrictions on images that can be
applied with development-time enforcement. Images violating any of the
restrictions will be rendered as placeholder images, which are easy to identify
and fix. These policies can be specified in report-only mode where images will
render normally without enforcement while violations are being observed via
reports. (See &lt;a href=&quot;#report-only-mode-in-the-wild&quot;&gt;Report-only mode in the wild&lt;/a&gt;,
below for details.)&lt;/p&gt;
&lt;h3 id=&quot;oversized-images&quot;&gt;oversized-images &lt;a class=&quot;w-headline-link&quot; href=&quot;#oversized-images&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;oversized-images&lt;/code&gt; feature policy restricts the intrinsic dimensions
of an image in relation to its container size.&lt;/p&gt;
&lt;p&gt;When a document uses the &lt;code&gt;oversized-images&lt;/code&gt; policy, any &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element
whose intrinsic resolution is more than X times larger than the container size
in either dimension will be replaced with a placeholder image.&lt;/p&gt;
&lt;h4 id=&quot;why&quot;&gt;Why? &lt;a class=&quot;w-headline-link&quot; href=&quot;#why&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Serving images larger than what the viewing device can render—for example,
serving desktop images to mobile contexts, or serving high-pixel-density images
to a low-pixel-density device—is wasting network traffic and device
memory. Read &lt;a href=&quot;https://web.dev/serve-images-with-correct-dimensions/&quot;&gt;Serve images with correct
dimensions&lt;/a&gt; and &lt;a href=&quot;https://web.dev/serve-responsive-images/&quot;&gt;Serve
responsive images&lt;/a&gt; for information on
optimizing your images.&lt;/p&gt;
&lt;h4 id=&quot;examples&quot;&gt;Examples &lt;a class=&quot;w-headline-link&quot; href=&quot;#examples&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A few examples illustrate this. The following shows the default behavior when cutting an image&#39;s display size in half.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/image-policies/default-100x100.png&quot; alt=&quot;The default resizing behavior&quot; style=&quot;max-width: 326px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    The default resizing behavior.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If I apply the following feature policy, I get a placeholder image instead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Feature-Policy: oversized-images *(2);&lt;/code&gt;&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/image-policies/resize-both-dimensions.png&quot; alt=&quot;When the image is too large for the container&quot; style=&quot;max-width: 326px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    When the image is too large for the container.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I get similar results if I lower only the width or the height.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/image-policies/resize-width.png&quot; alt=&quot;Resized width&quot; style=&quot;max-width: 326px;&quot;&gt;
  &lt;img src=&quot;https://web.dev/image-policies/resize-height.png&quot; alt=&quot;Resized height&quot; style=&quot;max-width: 326px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Resize width and height.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;how-to-use&quot;&gt;How to use &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-use&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To summarize, &lt;code&gt;oversized-images&lt;/code&gt; policy can be specified through either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Feature-Policy&lt;/code&gt; HTTP header&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; &lt;code&gt;allow&lt;/code&gt; attribute&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To declare the &lt;code&gt;oversized-images&lt;/code&gt; policy, you need to provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The feature name, &lt;code&gt;oversized-images&lt;/code&gt; (Required)&lt;/li&gt;
&lt;li&gt;A list of origins (Optional)&lt;/li&gt;
&lt;li&gt;The threshold values (i.e., the downscaling ratio X) for the origins, specified in
parenthesis (Optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We recommend a downscaling ratio of 2.0 or lower. Consider using &lt;a href=&quot;https://web.dev/serve-responsive-images/&quot;&gt;responsive
images&lt;/a&gt; with different resolutions to
best serve images on various screen sizes, resolutions, and so on.&lt;/p&gt;
&lt;h4 id=&quot;more-examples&quot;&gt;More examples &lt;a class=&quot;w-headline-link&quot; href=&quot;#more-examples&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Feature-Policy: oversized-images *(2.0)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The policy is enforced on all origins with a threshold value of 2.0. Any
&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element with an image whose downscaling ratio that is greater than
2.0 is disallowed and will be replaced with a placeholder image.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Feature-Policy: oversized-images *(inf) &#39;self&#39;(1.5)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The policy is enforced on the site&#39;s origin with a threshold value of 1.5.
&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements in top-level browsing contexts and same origin nested
browsing contexts will only render normally if the downscaling ratio is less
than or equal to 1.5. &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements everywhere else will render normally.&lt;/p&gt;
&lt;h3 id=&quot;unoptimized-lossylossless-images&quot;&gt;unoptimized-{lossy,lossless}-images &lt;a class=&quot;w-headline-link&quot; href=&quot;#unoptimized-lossylossless-images&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;unoptimized-lossy-images&lt;/code&gt;, &lt;code&gt;unoptimized-lossless-images&lt;/code&gt;,
&lt;code&gt;unoptimized-lossless-images-strict&lt;/code&gt; feature policies restrict the file
size of an image in relation to its intrinsic resolution:&lt;/p&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;code&gt;unoptimized-lossy-images&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Lossy formats should not exceed a byte-per-pixel ratio of X, with a fixed &lt;strong&gt;1KB&lt;/strong&gt; overhead allowance. For a W x H image, the file size threshold is calculated as W x H x X + 1024.&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;unoptimized-lossless-images&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Lossless formats should not exceed a byte-per-pixel ratio of X, with a fixed &lt;strong&gt;10KB&lt;/strong&gt; overhead allowance. For a W x H image, the file size threshold is calculated as W x H x X + 10240.&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;unoptimized-lossless-images-strict&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Lossless formats should not exceed a byte-per-pixel ratio of X, with a fixed &lt;strong&gt;1KB&lt;/strong&gt; overhead allowance. For a W x H image, the file size threshold is calculated as W x H x X + 1024.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;When a document uses any of these policies, any &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element violating
the constraint will be replaced with a placeholder image.&lt;/p&gt;
&lt;h4 id=&quot;why-2&quot;&gt;Why? &lt;a class=&quot;w-headline-link&quot; href=&quot;#why-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The larger the download size is, the longer it takes for an image to load. The
file size should be kept as small as possible when optimizing an image:
stripping metadata, picking a good image format, using image compression, and so on.
Read &lt;a href=&quot;https://web.dev/use-imagemin-to-compress-images/&quot;&gt;Use Imagemin to compress
images&lt;/a&gt; and &lt;a href=&quot;https://web.dev/serve-images-webp/&quot;&gt;Use WebP
images&lt;/a&gt; for information on optimizing your
images.&lt;/p&gt;
&lt;h4 id=&quot;example&quot;&gt;Example &lt;a class=&quot;w-headline-link&quot; href=&quot;#example&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The following shows the default browser behavior. Without the feature policy an unoptimized lossy image can be displayed just the same as an optimized image.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/image-policies/unoptimized-lossy.png&quot; alt=&quot;Comparing an optimized image with an unoptimized image&quot; style=&quot;max-width: 326px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Comparing an optimized image with an unoptimized image.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If I apply the following feature policy, I get a placeholder image instead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Feature-Policy: unoptimized-lossy-images *(0.5);&lt;/code&gt;&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/image-policies/lossy-image-excluded.png&quot; alt=&quot;When the image is not optimized&quot; style=&quot;max-width: 326px;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    When the image is not optimized.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;how-to-use-2&quot;&gt;How to use &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-to-use-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If you are new to feature policy, please check out &lt;a href=&quot;https://developers.google.com/web/updates/2018/06/feature-policy&quot;&gt;Introduction to Feature
Policy&lt;/a&gt; for
more details.&lt;/p&gt;
&lt;p&gt;To summarize, &lt;code&gt;unoptimized-{lossy,lossless}-images&lt;/code&gt; policies can be either
specified through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Feature-Policy&lt;/code&gt; HTTP header&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; &lt;code&gt;allow&lt;/code&gt; attribute&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To declare an &lt;code&gt;unoptimized-{lossy,lossless}-images&lt;/code&gt; policy, you will need to
provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The feature name, for example, &lt;code&gt;unoptimized-lossy-images&lt;/code&gt; (Required)&lt;/li&gt;
&lt;li&gt;A list of origins (Optional)&lt;/li&gt;
&lt;li&gt;The threshold values (i.e., byte-per-pixel ratio X) for the origins, specified
in parenthesis (Optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We recommend a byte-per-pixel ratio of 0.5 or lower for
&lt;code&gt;unoptimized-lossy-images&lt;/code&gt; and a byte-per-pixel ratio of 1 or lower for
&lt;code&gt;unoptimized-lossless-images&lt;/code&gt; and &lt;code&gt;unoptimized-lossless-images-strict&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;WebP formats have better compression ratios than other formats. Serve all your
images in WebP lossy format if you can. If that is not sufficient, try WebP
lossless format. Use JPEG on browsers that don&#39;t support WebP formats. Use PNG
if none of thes formats work.&lt;/p&gt;
&lt;p&gt;If you are using WebP formats, try with stricter thresholds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0.2 for WEBPV8&lt;/li&gt;
&lt;li&gt;0.5 for WEBPL&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;more-examples-2&quot;&gt;More examples &lt;a class=&quot;w-headline-link&quot; href=&quot;#more-examples-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Feature-Policy:  unoptimized-lossy-images *(0.5);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;                 unoptimized-lossless-images *(1.0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;                 unoptimized-lossless-images-strict *(1.0);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This policy is enforced on all origins with a threshold value of 0.5 (for lossy
formats) and 1 (for lossless formats). Any &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element whose image has a
byte-per-pixel ratio exceeding the constraint is disallowed and will be replaced
with a placeholder image.&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Feature-Policy: unoptimized-lossy-images *(inf) &#39;self&#39;(0.3);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;                unoptimized-lossless-images *(inf) &#39;self&#39;(0.8);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;                unoptimized-lossless-images-strict *(inf) &#39;self&#39;(0.8);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This policy is enforced on the site&#39;s origin with a threshold value of 0.3 (for
lossy formats) and 0.8 (for lossless formats). The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements in top-level
browsing contexts and same origin nested browsing contexts will only render
normally if the byte-per-pixel ratio meets these constraints. The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements
everywhere else will render normally.&lt;/p&gt;
&lt;h3 id=&quot;report-only-mode-in-the-wild&quot;&gt;Report-only mode in the wild &lt;a class=&quot;w-headline-link&quot; href=&quot;#report-only-mode-in-the-wild&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Publishing a site with placeholder images may not be what you want. You can use
the policies in enforcement mode (with unoptimized images rendered as
placeholder images) during development and staging, and use report-only mode in
production. (Check out &lt;a href=&quot;https://github.com/w3c/webappsec-feature-policy/blob/master/reporting.md&quot;&gt;Feature Policy
Reporting&lt;/a&gt;
for more details.) Similar to &lt;code&gt;Feature-Policy&lt;/code&gt; HTTP header, the
&lt;code&gt;Feature-Policy-Report-Only&lt;/code&gt; header lets you observe violation reports in the
wild without any enforcement.&lt;/p&gt;
&lt;h3 id=&quot;limitations&quot;&gt;Limitations &lt;a class=&quot;w-headline-link&quot; href=&quot;#limitations&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image policies only work on HTML image elements (&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;,
etc.) and are not yet supported on background images or generated content. If you
would like to have policies supported on broader contents, please let us know.&lt;/p&gt;
&lt;h2 id=&quot;optimizing-your-images&quot;&gt;Optimizing your images &lt;a class=&quot;w-headline-link&quot; href=&quot;#optimizing-your-images&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve talked quite a bit about optimizing your images, but haven&#39;t said how to do it. That topic is out of scope for this article, but you can learn more from the links below and from the codelabs listed at the end of the article.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/fast#optimize-your-images&quot;&gt;Optimizing images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/serve-images-with-correct-dimensions/&quot;&gt;Serve images with correct dimentions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;experiment-with-the-policies-in-origin-trials&quot;&gt;Experiment with the policies in origin trials &lt;a class=&quot;w-headline-link&quot; href=&quot;#experiment-with-the-policies-in-origin-trials&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image policies are available in Chrome 75 via an origin trial.&lt;/p&gt;
&lt;p&gt;To participate:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.chrome.com/origintrials/#/view_trial/2562548187973812225&quot;&gt;request a token&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the token on any pages in your origin using an &lt;code&gt;Origin-Trial&lt;/code&gt; HTTP
header:&lt;br&gt;
&lt;br&gt;
&lt;code&gt;Origin-Trial: **token as provided in the developer console**&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Specify an image policy via HTTP header Feature-Policy header:&lt;br&gt;
&lt;br&gt;
&lt;code&gt;Feature-Policy: **image policies specified here**&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Check out  &lt;a href=&quot;https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md&quot;&gt;Origin Trials Guide for Web
Developers&lt;/a&gt;
for more details.&lt;/p&gt;
&lt;h2 id=&quot;please-give-us-feedback&quot;&gt;Please give us feedback &lt;a class=&quot;w-headline-link&quot; href=&quot;#please-give-us-feedback&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hopefully this article has given you a good understanding of the image policies
and gotten you excited. We&#39;d really love for you to try out the policies and
give us feedback.&lt;/p&gt;
&lt;p&gt;You can give us feedback for each of the features mentioned in this article to
our mailing list: &lt;a href=&quot;mailto:feature-control@chromium.org&quot;&gt;feature-control@&lt;/a&gt;&lt;a href=&quot;mailto:feature-control@chromium.org&quot;&gt;chromium.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We would love to know what threshold values you used and found useful. We would
love to know whether &lt;code&gt;unoptimized-lossless-images&lt;/code&gt; or
&lt;code&gt;unoptimized-lossless-images-strict&lt;/code&gt; is more intuitive and easy to use, or if we
should use a difference overhead allowance instead. We will be sending out a
survey near the end of the trial. Stay tuned!&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Extract critical CSS</title>
    <link href="https://web.dev/extract-critical-css/"/>
    <updated>2019-05-28T17:00:00-07:00</updated>
    <id>https://web.dev/extract-critical-css/</id>
    <content type="html">&lt;p&gt;The browser must download and parse CSS files before it can show the page, which makes CSS a render-blocking resource. If CSS files are big, or network conditions are poor, requests for CSS files can significantly increase the time it takes for a web page to render.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--key-term&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key Term:&lt;/strong&gt;
Critical CSS is a technique that extracts the CSS for above-the-fold content in order to render content to the user as fast as possible.&lt;/p&gt;
&lt;/div&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img class=&quot;&quot; src=&quot;https://web.dev/extract-critical-css/above-the-fold.png&quot; alt=&quot;An illustration of a laptop and a mobile device with web pages overflowing the edges of screens&quot; style=&quot;max-width: 600px; width: 100%;&quot;&gt;
&lt;/figure&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Above-the-fold is all the content a viewer sees on page load, before scrolling. There is no universally defined pixel height of what is considered above the fold content since there is a myriad of devices and screen sizes.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Inlining extracted styles in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the HTML document eliminates the need to make an additional request to fetch these styles. The remainder of the CSS can be loaded asynchronously.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
    &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/extract-critical-css/inline-critical-css.png&quot; alt=&quot;HTML file with critical CSS inlined in the head&quot;&gt;
    &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Inlined critical CSS
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Improving render times can make a huge difference in &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/rail#ux&quot;&gt;perceived performance&lt;/a&gt;, especially under poor network conditions. On mobile networks, high latency is an issue regardless of bandwidth.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/extract-critical-css/filmstrip-comparison.png&quot; alt=&quot;Filmstrip view comparison of loading a page with render-blocking CSS (top) and the same page with inlined critical CSS (bottom) on a 3G connection. Top filmstrip shows six blank frames before finally displaying content. Bottom filmstrip displays meaningful content in the first frame.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Comparison of loading a page with render-blocking CSS (top) and the same page with inlined critical CSS (bottom) on a 3G connection
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you have poor &lt;a href=&quot;https://web.dev/first-contentful-paint/&quot;&gt;First Contentful Paint&lt;/a&gt; (FCP) and see &amp;quot;Eliminate render-blocking resource&amp;quot; opportunity in Lighthouse audits it&#39;s a good idea to give critical CSS a go.&lt;/p&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/extract-critical-css/lighthouse-audit.png&quot; alt=&quot;Lighthouse audit with &#39;Eliminate render-blocking resource&#39; or &#39;Defer unused CSS&#39; opportunities&quot;&gt;
&lt;div class=&quot;w-aside w-aside--gotchas&quot;&gt;
&lt;strong&gt;Gotchas!&lt;/strong&gt; &lt;p&gt;Keep in mind that if you inline a large amount of CSS, it delays the transmission of the rest of the HTML document. If everything is prioritized then nothing is. Inlining also has some downsides in that it prevents the browser from caching the CSS for reuse on subsequent page loads, so it&#39;s best to use it sparingly.&lt;/p&gt;
&lt;/div&gt;
&lt;p id=&quot;14KB&quot;&gt;To minimize the number of roundtrips to first render, aim to keep above-the-fold content under &lt;strong&gt;14 KB&lt;/strong&gt; (compressed).&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;New &lt;a href=&quot;https://hpbn.co/building-blocks-of-tcp/&quot;&gt;TCP&lt;/a&gt; connections cannot immediately use the full available bandwidth between the client and the server, they all go through &lt;a href=&quot;https://hpbn.co/building-blocks-of-tcp/#slow-start&quot;&gt;slow-start&lt;/a&gt; to avoid overloading the connection with more data than it can carry. In this process, the server starts the transfer with a small amount of data and if it reaches the client in perfect condition, doubles the amount in the next roundtrip. For most servers, 10 packets or approximately 14 KB is the maximum that can be transferred in the first roundtrip.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The performance impact you can achieve with this technique depends on the type of your website. Generally speaking, the more CSS a site has, the greater the possible impact of inlined CSS.&lt;/p&gt;
&lt;h2 id=&quot;overview-of-tools&quot;&gt;Overview of tools &lt;a class=&quot;w-headline-link&quot; href=&quot;#overview-of-tools&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of great tools that can automatically determine the critical CSS for a page. This is good news because doing this manually would be a tedious process. It requires analysis of the entire DOM to determine the styles that are applied to each element in the viewport.&lt;/p&gt;
&lt;h3 id=&quot;critical&quot;&gt;Critical &lt;a class=&quot;w-headline-link&quot; href=&quot;#critical&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/addyosmani/critical&quot;&gt;Critical&lt;/a&gt; extracts, minifies and inlines above-the-fold CSS and is available as &lt;a href=&quot;https://www.npmjs.com/package/critical&quot;&gt;npm module&lt;/a&gt;. It can be used with Gulp (directly) or with Grunt (as a &lt;a href=&quot;https://github.com/bezoerb/grunt-critical&quot;&gt;plugin&lt;/a&gt;) and there&#39;s a &lt;a href=&quot;https://github.com/anthonygore/html-critical-webpack-plugin&quot;&gt;webpack plugin&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;It&#39;s a simple tool that takes a lot of thinking out of the process. You don&#39;t even have to specify the stylesheets, Critical automatically detects them. It also supports extracting critical CSS for multiple screen resolutions.&lt;/p&gt;
&lt;h3 id=&quot;criticalcss&quot;&gt;criticalCSS &lt;a class=&quot;w-headline-link&quot; href=&quot;#criticalcss&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/filamentgroup/criticalCSS&quot;&gt;CriticalCSS&lt;/a&gt; is another &lt;a href=&quot;https://www.npmjs.com/package/criticalcss&quot;&gt;npm module&lt;/a&gt; that extracts above-the-fold CSS. It is also available as a CLI.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t have options to inline and minify critical CSS, but it does let you force-include rules that don&#39;t actually belong in critical CSS and gives you more granular control over including &lt;code&gt;@font-face&lt;/code&gt; declarations.&lt;/p&gt;
&lt;h3 id=&quot;penthouse&quot;&gt;Penthouse &lt;a class=&quot;w-headline-link&quot; href=&quot;#penthouse&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/pocketjoso/penthouse&quot;&gt;Penthouse&lt;/a&gt; is a good choice if your site or app has a large number of styles or styles which are being dynamically injected into the DOM (common in Angular apps). It uses &lt;a href=&quot;https://github.com/GoogleChrome/puppeteer&quot;&gt;Puppeteer&lt;/a&gt; under the hood and even features an &lt;a href=&quot;https://jonassebastianohlsson.com/criticalpathcssgenerator/&quot;&gt;online hosted version&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;Penthouse doesn&#39;t detect stylesheets automatically, you have to specify the HTML and CSS files that you want to generate critical CSS for. The upside is that it&#39;s good at running many jobs in parallel.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Are long JavaScript tasks delaying your Time to Interactive?</title>
    <link href="https://web.dev/long-tasks-devtools/"/>
    <updated>2019-05-28T17:00:00-07:00</updated>
    <id>https://web.dev/long-tasks-devtools/</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;tl;dr: Long Tasks can keep the main thread busy, delaying user interaction. Chrome DevTools can now visualize Long Tasks, making it easier to see tasks to optimize.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you use Lighthouse to audit your pages, you may be familiar with &lt;a href=&quot;https://web.dev/interactive&quot;&gt;Time to Interactive&lt;/a&gt;, a metric representing when users can interact with your page and get a response. But did you know Long (JavaScript) Tasks can contribute heavily to a poor TTI?&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;w-screenshot&quot; sizes=&quot;(max-width: 1400px) 100vw, 1400px&quot; srcset=&quot;https://web.dev/long-tasks-devtools/Are-long0_rq2bce_c_scale_w_200.png 200w, https://web.dev/long-tasks-devtools/Are-long0_rq2bce_c_scale_w_775.png 775w, https://web.dev/long-tasks-devtools/Are-long0_rq2bce_c_scale_w_1239.png 1239w, https://web.dev/long-tasks-devtools/Are-long0_rq2bce_c_scale_w_1400.png 1400w&quot; src=&quot;https://web.dev/long-tasks-devtools/Are-long0_rq2bce_c_scale_w_1400.png&quot; alt=&quot;Time to Interactive displayed in the Lighthouse Report&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-are-long-tasks&quot;&gt;What are Long Tasks? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-are-long-tasks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Long_Tasks_API&quot;&gt;Long Task&lt;/a&gt; is JavaScript code that monopolizes the main thread for extended periods of time, causing the UI to &amp;quot;freeze&amp;quot;.&lt;/p&gt;
&lt;p&gt;While a web page is loading, Long Tasks can tie up the main thread and make the page unresponsive to user input even if it looks ready. Clicks and taps often don&#39;t work because event listeners, click handlers etc have not yet been attached.&lt;/p&gt;
&lt;p&gt;CPU-heavy Long Tasks occur due to complex work that takes longer than 50ms. Why 50ms? &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/rail&quot;&gt;The RAIL model&lt;/a&gt; suggests you process user input events in &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/rail#response&quot;&gt;50ms&lt;/a&gt; to ensure a visible response within 100ms. If you don&#39;t, the connection between action and reaction is broken.&lt;/p&gt;
&lt;h2 id=&quot;are-there-long-tasks-in-my-page-that-could-delay-interactivity&quot;&gt;Are there Long Tasks in my page that could delay interactivity? &lt;a class=&quot;w-headline-link&quot; href=&quot;#are-there-long-tasks-in-my-page-that-could-delay-interactivity&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Until now, you&#39;ve needed to manually look for &amp;quot;long yellow blocks&amp;quot; of script over 50ms long in &lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/&quot;&gt;Chrome DevTools&lt;/a&gt; or use the &lt;a href=&quot;https://calendar.perfplanet.com/2017/tracking-cpu-with-long-tasks-api/&quot;&gt;Long Tasks API&lt;/a&gt; to figure out what tasks were delaying interactivity. This could be a little cumbersome.&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;w-screenshot&quot; sizes=&quot;(max-width: 1400px) 100vw, 1400px&quot; srcset=&quot;https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_200.png 200w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_349.png 349w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_457.png 457w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_570.png 570w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_670.png 670w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_755.png 755w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_841.png 841w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_919.png 919w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1000.png 1000w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1077.png 1077w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1153.png 1153w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1220.png 1220w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1289.png 1289w, https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1400.png 1400w&quot; src=&quot;https://web.dev/long-tasks-devtools/Are-long1_yp7hwf_c_scale_w_1400.png&quot; alt=&quot;A DevTools Performance panel screenshot showing the differences between short tasks and long tasks&quot;&gt;&lt;/p&gt;
&lt;p&gt;To help ease your performance auditing workflow, &lt;a href=&quot;https://developers.google.com/web/updates/2019/03/devtools#longtasks&quot;&gt;DevTools now visualizes Long Tasks&lt;/a&gt;. Tasks (shown in gray) have red flags if they are Long Tasks.&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;w-screenshot&quot; sizes=&quot;(max-width: 1400px) 100vw, 1400px&quot; srcset=&quot;https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_200.png 200w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_424.png 424w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_600.png 600w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_740.png 740w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_884.png 884w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_1020.png 1020w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_1136.png 1136w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_1254.png 1254w, https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_1400.png 1400w&quot; src=&quot;https://web.dev/long-tasks-devtools/Are-long2_momntc_c_scale_w_1400.png&quot; alt=&quot;DevTools visualizing Long Tasks as gray bars in the Performance Panel with a red flag for long tasks&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Record a trace in the &lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/&quot;&gt;Performance panel&lt;/a&gt; of loading up a web page.&lt;/li&gt;
&lt;li&gt;Look for a red flag in the main thread view. You should see tasks are now gray (&amp;quot;Task&amp;quot;).&lt;/li&gt;
&lt;li&gt;Hovering over a bar will let you know the duration of the task and if it was considered &amp;quot;long&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-causing-my-long-tasks&quot;&gt;What is causing my Long Tasks? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-is-causing-my-long-tasks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To discover what is causing a long task, select the gray &lt;strong&gt;Task&lt;/strong&gt; bar. In the drawer beneath, select &lt;strong&gt;Bottom-Up&lt;/strong&gt; and &lt;strong&gt;Group by Activity&lt;/strong&gt;. This allows you to see what activities contributed the most (in total) to the task taking so long to complete. Below, it appears to be a costly set of DOM queries.&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;w-screenshot&quot; sizes=&quot;(max-width: 1400px) 100vw, 1400px&quot; srcset=&quot;https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_200.png 200w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_394.png 394w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_547.png 547w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_678.png 678w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_786.png 786w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_904.png 904w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_1010.png 1010w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_1109.png 1109w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_1212.png 1212w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_1312.png 1312w, https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_1400.png 1400w&quot; src=&quot;https://web.dev/long-tasks-devtools/Are-long3_tfm3wr_c_scale_w_1400.png&quot; alt=&quot;Selecting a long task (labelled &#39;Task&#39;) in DevTools allows us to drill-down into the activities that were responsible for it.&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-are-common-ways-to-optimize-long-tasks&quot;&gt;What are common ways to optimize Long Tasks? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-are-common-ways-to-optimize-long-tasks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Large scripts are often a major cause of Long Tasks so consider &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting&quot;&gt;splitting them up&lt;/a&gt;. Also keep an eye on third-party scripts; their Long Tasks can delay primary content from getting interactive.&lt;/p&gt;
&lt;p&gt;Break all your work into small chunks (that run in &amp;lt; 50ms) and run these chunks at the right place and time; the right place may even be off the main thread, in a worker. Phil Walton&#39;s &lt;a href=&quot;https://philipwalton.com/articles/idle-until-urgent/&quot;&gt;Idle Until Urgent&lt;/a&gt; is a good read on this topic.&lt;/p&gt;
&lt;p&gt;Keep your pages responsive. Minimizing Long Tasks is a great way to ensure your users have a delightful experience when they visit your site. For more on Long Tasks, check out &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#tracking_long_tasks&quot;&gt;User-centric Performance Metrics&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Speed at scale&amp;#58; what&#39;s new in web performance?</title>
    <link href="https://web.dev/speed-at-scale/"/>
    <updated>2019-05-23T17:00:00-07:00</updated>
    <id>https://web.dev/speed-at-scale/</id>
    <content type="html">&lt;p&gt;During the &lt;a href=&quot;https://www.youtube.com/watch?v=YJGCZCaIZkQ&amp;amp;feature=youtu.be&quot;&gt;&amp;quot;Speed at Scale&amp;quot;
talk&lt;/a&gt; at Google
I/O 2019, we announced three things that we hope will improve web performance
over the coming year.&lt;/p&gt;
&lt;div style=&quot;width:100%; padding-top: 56.25%; position: relative;&quot;&gt;
&lt;iframe style=&quot;width:100%; height: 100%;position: absolute; top: 50%; left:
50%; transform: translate(-50%,-50%);&quot; src=&quot;https://www.youtube.com/embed/YJGCZCaIZkQ&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;lighthouse-now-supports-performance-budgeting&quot;&gt;Lighthouse now supports Performance Budgeting &lt;a class=&quot;w-headline-link&quot; href=&quot;#lighthouse-now-supports-performance-budgeting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/audits/budgets&quot;&gt;LightWallet&lt;/a&gt;
is a new feature in Lighthouse that adds support for &lt;a href=&quot;https://web.dev/fast#set-performance-budgets&quot;&gt;performance
budgets&lt;/a&gt;. Performance budgets establish
standards for the performance of your site. More importantly, they make it is
easy to identify and fix performance regressions before they ship.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/speed-at-scale/Speed-at0.png&quot; alt=&quot;A LightWallet report showing which assets are over the file size budget.&quot; loading=&quot;lazy&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;LightWallet is available in the newest version of the Lighthouse CLI and only
takes a couple minutes to set up. These&lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/audits/budgets&quot;&gt;
instructions&lt;/a&gt;
provide more information.&lt;/p&gt;
&lt;p&gt;Unsure what your budgets should be? Try our experimental &lt;a href=&quot;https://bit.ly/perf-budget-calculator&quot;&gt;Performance Budget
Calculator&lt;/a&gt; which can generate a
LightWallet compatible budget configuration.&lt;/p&gt;
&lt;h2 id=&quot;native-image-and-iframe-lazy-loading-comes-to-the-web&quot;&gt;Native image and iframe lazy-loading comes to the web &lt;a class=&quot;w-headline-link&quot; href=&quot;#native-image-and-iframe-lazy-loading-comes-to-the-web&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web pages often contain a large number of images, which contribute to
data-usage, &lt;a href=&quot;https://httparchive.org/reports/state-of-images&quot;&gt;page-bloat&lt;/a&gt; and
slower page loads. Many of these images are offscreen, requiring a user to
scroll in order to view them.&lt;/p&gt;
&lt;p&gt;Until now, you&#39;ve needed to solve lazy-loading images using a JavaScript
library but that may soon change. This summer, Chrome will be launching support
for the &lt;a href=&quot;https://addyosmani.com/blog/lazy-loading/&quot;&gt;loading&lt;/a&gt; attribute which
brings native &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; lazy-loading to the web.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
&lt;img src=&quot;https://web.dev/speed-at-scale/Speed-at1.png&quot; alt=&quot;Native lazy-loading highlighting offscreen
content being loaded on-demand&quot; loading=&quot;lazy&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute allows a browser to defer loading offscreen images and
iframes until users scroll near them. &lt;code&gt;loading&lt;/code&gt; supports three values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt;: is a good candidate for lazy loading.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eager&lt;/code&gt;: is not a good candidate for lazy loading. Load right away.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;: browser will determine whether or not to lazily load.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;io2019.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;iframe&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video-player.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The exact heuristics for &amp;quot;when the user scrolls near&amp;quot; is left up to the
browser. In general, our hope is that browsers will start fetching deferred
images and iframe content a little before it comes into the viewport.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute is implemented behind flags in Chrome Canary. You can
try out &lt;a href=&quot;https://mathiasbynens.be/demo/img-loading-lazy&quot;&gt;this demo&lt;/a&gt; in Chrome
75+ with the &lt;code&gt;chrome://flags/#enable-lazy-image-loading&lt;/code&gt; and
&lt;code&gt;chrome://flags/#enable-lazy-frame-loading&lt;/code&gt; flags turned on.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://addyosmani.com/blog/lazy-loading/&quot;&gt;write-up&lt;/a&gt; on the native
lazy-loading feature is available with more details.&lt;/p&gt;
&lt;h2 id=&quot;google-fonts-now-supports-font-display-as-a-query-parameter&quot;&gt;Google Fonts now supports font-display as a query parameter &lt;a class=&quot;w-headline-link&quot; href=&quot;#google-fonts-now-supports-font-display-as-a-query-parameter&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We announced support for &lt;a href=&quot;https://font-display.glitch.me/&quot;&gt;font-display&lt;/a&gt; is now available in production for all &lt;a href=&quot;https://fonts.google.com/&quot;&gt;Google Fonts&lt;/a&gt; via the &lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started#use_font-display&quot;&gt;display query-string parameter&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;https://fonts.googleapis.com/css?family=Lobster&amp;amp;display=swap&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;font-display&lt;/code&gt; descriptor lets you decide how your web fonts will render or
fallback, depending on how long it takes for them to load. It supports a number
of values including &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;block&lt;/code&gt;, &lt;code&gt;swap&lt;/code&gt;, &lt;code&gt;fallback&lt;/code&gt; and &lt;code&gt;optional&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Previously, the only way to specify &lt;code&gt;font-display&lt;/code&gt; for web fonts from Google Fonts was to self-host them but this change removes the need to do so.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started#use_font-display&quot;&gt;Google Fonts
documentation&lt;/a&gt;
has been updated to include &lt;code&gt;font-display&lt;/code&gt; in the default code embeds (as seem
below). We hope this will encourage more developers to try out this exciting
addition.&lt;/p&gt;
&lt;figure class=&quot;w-figure&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/speed-at-scale/Speed-at2.png&quot; alt=&quot;Google Fonts embed code with font-display included in the URL as a query-parameter&quot; loading=&quot;lazy&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Here&#39;s a &lt;a href=&quot;https://glitch.com/~truth-bookcase&quot;&gt;demo&lt;/a&gt; on Glitch of using display
with multiple font families. Try it out with &lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/network/#throttle&quot;&gt;DevTools network
emulation&lt;/a&gt;
to see the impact of &lt;code&gt;font-display: swap&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;watch-for-more&quot;&gt;Watch for more &lt;a class=&quot;w-headline-link&quot; href=&quot;#watch-for-more&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our talk also covered several production case studies of using advanced
performance patterns to improve user-experience. These included sites
leveraging image CDNs, &lt;a href=&quot;https://web.dev/fast/reduce-network-payloads-using-text-compression/codelab-text-compression-brotli&quot;&gt;Brotli
compression&lt;/a&gt;,
smart JavaScript serving and prefetching to speed up their pages. &lt;a href=&quot;https://www.youtube.com/watch?v=YJGCZCaIZkQ&amp;amp;feature=youtu.be&quot;&gt;Watch the
talk&lt;/a&gt; to learn
more :)&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Introducing PROXX</title>
    <link href="https://web.dev/proxx-announce/"/>
    <updated>2019-05-08T17:00:00-07:00</updated>
    <id>https://web.dev/proxx-announce/</id>
    <content type="html">&lt;p&gt;The team that brought you &lt;a href=&quot;https://squoosh.app/&quot;&gt;squoosh.app&lt;/a&gt; is back! This time,
we built a web-based game called PROXX (&lt;a href=&quot;https://proxx.app/&quot;&gt;proxx.app&lt;/a&gt;). PROXX
is a game of proximity inspired by the legendary game Minesweeper. The game is
situated in the space and your job is to find out where the black holes are. It
works on all kinds of devices—from desktop all the way to feature phones.
Users can play the game using a mouse, keyboard, d-pad even with a screen
reader.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
&lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; poster=&quot;https://storage.googleapis.com/webfundamentals-assets/proxx-announce-blogpost/poster.jpg&quot;&gt;
  &lt;source src=&quot;https://storage.googleapis.com/webfundamentals-assets/proxx-announce-blogpost/kaios_vp8.webm&quot; type=&quot;video/webm; codecs=vp8&quot;&gt;
  &lt;source src=&quot;https://storage.googleapis.com/webfundamentals-assets/proxx-announce-blogpost/kaios_x264.mp4&quot; type=&quot;video/mp4; codecs=h264&quot;&gt;
&lt;/video&gt;
 &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    PROXX on a feature phone.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;our-baseline&quot;&gt;Our baseline &lt;a class=&quot;w-headline-link&quot; href=&quot;#our-baseline&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before building this game, we set the following goals and budgets for the
application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same core experience&lt;/strong&gt;: all devices must function the same way&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accessible&lt;/strong&gt;: mouse, keyboard, touch, d-pad, screen readers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performant&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Less than 25kb of initial payload&lt;/li&gt;
&lt;li&gt;Less than 5 seconds TTI (&lt;a href=&quot;https://web.dev/interactive&quot;&gt;time to interactive&lt;/a&gt;)
on slow 3G&lt;/li&gt;
&lt;li&gt;Consistent 60fps animation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/proxx-announce/pixelbook.jpg&quot; alt=&quot;A pixelbook running PROXX&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    PROXX on a pixelbook.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;web-workers&quot;&gt;Web Workers &lt;a class=&quot;w-headline-link&quot; href=&quot;#web-workers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The game consists of 4 main entities, the core game logic, the UI service, the
state service, and the animation graphics. Since we knew from the get-go we
would have to run heavily animated graphics on the main thread, we moved the
game logic and state service to a web worker in order to keep the main thread as
free as possible.&lt;/p&gt;
&lt;h2 id=&quot;build-time-pre-render&quot;&gt;Build time pre-render &lt;a class=&quot;w-headline-link&quot; href=&quot;#build-time-pre-render&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our UI is built with &lt;a href=&quot;https://preactjs.com/&quot;&gt;Preact&lt;/a&gt;, as it allows us to hit our
aggressive target for an initial payload that is less than 25kb. In order to
give a good initial loading experience, we decided to pre-render our 1st view.
We prerender at build time using &lt;a href=&quot;https://pptr.dev/&quot;&gt;Puppeteer&lt;/a&gt; to access the
top page and let preact populate the DOM. The resulting DOM is then serialized
to HTML and saved as index.html&lt;/p&gt;
&lt;h2 id=&quot;canvas-for-animation-(invisible)-dom-for-accessibility&quot;&gt;Canvas for animation, (invisible) DOM For accessibility &lt;a class=&quot;w-headline-link&quot; href=&quot;#canvas-for-animation-(invisible)-dom-for-accessibility&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We render the game graphics in a canvas using
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt;. One canvas
is responsible for the background animation and another one canvas for the game
grid on top. We also have an HTML table with buttons for accessibility reasons,
that is on top of both of these canvases, but is made invisible (opacity: 0).
Even though what you see is a canvas rendering of the game state, the player is
interacting with the invisible DOM table, giving us the ability to attach event
listeners and rely on the browser&#39;s focus management.&lt;/p&gt;
&lt;p&gt;By keeping the DOM element in the canvas, we are able to tap into browsers
native accessibility features. For example: by setting &lt;code&gt;role=&amp;quot;grid&amp;quot;&lt;/code&gt; on our game
table, screen readers can announce the row and column of the focused cell
without us having to implement that.&lt;/p&gt;
&lt;h2 id=&quot;rollup-for-bundling-and-code-splitting&quot;&gt;Rollup for bundling and code splitting &lt;a class=&quot;w-headline-link&quot; href=&quot;#rollup-for-bundling-and-code-splitting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our total size for the app comes down to 100KB gzipped. Out of that, 20KB is for
the initial payload (index.html). We use &lt;a href=&quot;https://rollupjs.org/&quot;&gt;Rollup.js&lt;/a&gt; for
this project. We have shared dependencies between the main thread and our web
worker, and Rollup can put these shared dependencies in a separate chunk that
only needs to be loaded once. Other bundlers like webpack duplicate the shared
dependencies which results in double-loading.&lt;/p&gt;
&lt;h2 id=&quot;supporting-feature-phones&quot;&gt;Supporting feature phones &lt;a class=&quot;w-headline-link&quot; href=&quot;#supporting-feature-phones&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Smart feature phones such as &lt;a href=&quot;https://www.kaiostech.com/&quot;&gt;KaiOS&lt;/a&gt; phones are
rapidly gaining popularity. These are very resource constrained devices, but our
approach of using web workers whenever we can allowed us to make the experience
highly responsive on these phones as well. Since feature phones come with
different input interface (d-pad and number keys, no touchscreen), we also
implemented key-based interface.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;img src=&quot;https://web.dev/proxx-announce/featurephone.jpg&quot; alt=&quot;A man playing PROXX on a yellow feature phone&quot; class=&quot;screenshot&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    PROXX on a feature phone.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;what&#39;s-next&quot;&gt;What&#39;s next &lt;a class=&quot;w-headline-link&quot; href=&quot;#what&#39;s-next&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We had great but busy time building this game in time for Google I/O 2019, so we
will take some well-deserved time off to rest, but plan to come back with more
in-depth documentation on each of these areas of the game.&lt;/p&gt;
&lt;p&gt;Until then, please check the talk Mariko gave at I/O on this project.&lt;/p&gt;
&lt;div style=&quot;width:100%; padding-top: 56.25%; position: relative;&quot;&gt;
  &lt;iframe style=&quot;width:100%; height: 100%;position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);&quot; src=&quot;https://www.youtube.com/embed/w8P5HLxcIO4&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;You can browse the code at &lt;a href=&quot;https://github.com/GoogleChromeLabs/proxx&quot;&gt;the proxx github repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Cheers! Surma, Jake, Mariko&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>SameSite cookies explained</title>
    <link href="https://web.dev/samesite-cookies-explained/"/>
    <updated>2019-05-06T17:00:00-07:00</updated>
    <id>https://web.dev/samesite-cookies-explained/</id>
    <content type="html">&lt;p&gt;Cookies are one of the methods available for adding persistent state to web
sites. Each cookie is a &lt;code&gt;key=value&lt;/code&gt; pair along with a number of attributes that
control when and where that cookie is used. You&#39;ve probably already used these
attributes to set things like expiry dates or indicating the cookie should only
be sent over HTTPS. Servers set cookies by sending the aptly-named &lt;code&gt;Set-Cookie&lt;/code&gt;
header in their response. For all the detail you can dive into
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1&quot;&gt;RFC6265bis&lt;/a&gt;,
but for now here&#39;s a quick refresher.&lt;/p&gt;
&lt;p&gt;Say you have a blog where you want to display a &amp;quot;What&#39;s new&amp;quot; promo to your
users. Users can dismiss the promo and then they won&#39;t see it again for a while.
You can store that preference in a cookie, set it to expire in a month
(2,600,000 seconds), and only send it over HTTPS. That header would look like
this:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: promo_shown=1; Max-Age=2600000; Secure&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/samesite-cookies-explained/set-cookie-response-header.png&quot; alt=&quot;Three cookies being sent to a
    browser from a server in a response&quot; style=&quot;max-width: 60vw&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Servers set cookies using the &lt;tt&gt;Set-Cookie&lt;/tt&gt; header.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When your reader views a page that meets those requirements, i.e. they&#39;re on a
secure connection and the cookie is less than a month old, then their browser
will send this header in its request:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Cookie: promo_shown=1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/samesite-cookies-explained/cookie-request-header.png&quot; alt=&quot;Three cookies being sent from a
    browser to a server in a request&quot; style=&quot;max-width: 60vw;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Your browser sends cookies back in the &lt;tt&gt;Cookie&lt;/tt&gt; header.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can also add and read the cookies available to that site in JavaScript using
&lt;code&gt;document.cookie&lt;/code&gt;. Making an assignment to &lt;code&gt;document.cookie&lt;/code&gt; will create or
override a cookie with that key. For example, you can try the following in your
browser&#39;s JavaScript console:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&gt; document.cookie = &quot;promo_shown=1; Max-Age=2600000; Secure&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt; &quot;promo_shown=1; Max-Age=2600000; Secure&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading &lt;code&gt;document.cookie&lt;/code&gt; will output all the cookies accessible in the current
context, with each cookie separated by a semicolon:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&gt; document.cookie;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt; &quot;promo_shown=1; color_theme=peachpuff; sidebar_loc=left&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/samesite-cookies-explained/document-cookie.png&quot; alt=&quot;Javascript accessing cookies within the
    browser&quot; style=&quot;max-width: 35vw;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    JavaScript can access cookies using &lt;tt&gt;document.cookie&lt;/tt&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you try this on a selection of popular sites you will notice that most of
them set significantly more than just three cookies. In most cases, those
cookies are sent on every single request to that domain, which has a number
of implications. Upload bandwidth is often more restricted than download for
your users, so that overhead on all outbound requests is adding a delay on your
time to first byte. Be conservative in the number and size of cookies you set.
Make use of the &lt;code&gt;Max-Age&lt;/code&gt; attribute to help ensure that cookies don&#39;t hang
around longer than needed.&lt;/p&gt;
&lt;h2 id=&quot;what-are-first-party-and-third-party-cookies&quot;&gt;What are first-party and third-party cookies? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-are-first-party-and-third-party-cookies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you go back to that same selection of sites you were looking at before, you
probably noticed that there were cookies present for a variety of domains, not
just the one you were currently visiting. Cookies that match the domain of the
current site, i.e. what&#39;s displayed in the browser&#39;s address bar, are referred
to as &lt;strong&gt;first-party&lt;/strong&gt; cookies. Similarly, cookies from domains other than the
current site are referred to as &lt;strong&gt;third-party&lt;/strong&gt; cookies. This isn&#39;t an absolute
label but is relative to the user&#39;s context; the same cookie can be either
first-party or third-party depending on which site the user is on at the time.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/samesite-cookies-explained/cross-site-set-cookie-response-header.png&quot; alt=&quot;Three cookies being
    sent to a browser from different requests on the same page&quot; style=&quot;max-width: 60vw;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Cookies may come from a variety of different domains on one page.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Continuing the example from above, let&#39;s say one of your blog posts has a
picture of a particularly amazing cat in it and it&#39;s hosted at
&lt;code&gt;/blog/img/amazing-cat.png&lt;/code&gt;. Because it&#39;s such an amazing image, another person
uses it directly on their site. If a visitor has been to your blog and has the
&lt;code&gt;promo_shown&lt;/code&gt; cookie, then when they view &lt;code&gt;amazing-cat.png&lt;/code&gt; on the other
person&#39;s site that cookie &lt;strong&gt;will be sent&lt;/strong&gt; in that request for the image. This
isn&#39;t particularly useful for anyone since &lt;code&gt;promo_shown&lt;/code&gt; isn&#39;t used for anything
on this other person&#39;s site, it&#39;s just adding that overhead to the request.&lt;/p&gt;
&lt;p&gt;If that&#39;s an unintended effect, why would you want to do this? It&#39;s this
mechanism that allows sites to maintain state when they are being used in a
third-party context. For example, if you embed a YouTube video on your site then
visitors will see a &amp;quot;Watch later&amp;quot; option in the player. If your visitor is
already signed in to YouTube, that session is being made available in the
embedded player by a third-party cookie—meaning that &amp;quot;Watch later&amp;quot; button will
just save the video in one go rather than prompting them to sign in or having to
navigate them away from your page and back over to YouTube.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/samesite-cookies-explained/cross-site-cookie-request-header.png&quot; alt=&quot;The same cookie being
    sent in three different contexts&quot; style=&quot;max-width: 60vw;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    A cookie in a third-party context is sent when visiting different pages.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One of the cultural properties of the web is that it&#39;s tended to be open by
default. This is part of what has made it possible for so many people to create
their own content and apps there. However, this has also brought a number of
security and privacy concerns. Cross-site request forgery (CSRF) attacks rely on
the fact that cookies are attached to any request to a given origin, no matter
who initiates the request. For example, if you visit &lt;code&gt;evil.example&lt;/code&gt; then it can
trigger requests to &lt;code&gt;your-blog.example&lt;/code&gt;, and your browser will happily attach
the associated cookies. If your blog isn&#39;t careful with how it validates those
requests then &lt;code&gt;evil.example&lt;/code&gt; could trigger actions like deleting posts or adding
their own content.&lt;/p&gt;
&lt;p&gt;Users are also becoming more aware of how cookies can be used to track their
activity across multiple sites. However until now there hasn&#39;t been a way to
explicitly state your intent with the cookie. Your &lt;code&gt;promo_shown&lt;/code&gt; cookie should
only be sent in a first-party context, whereas a session cookie for a widget
meant to be embedded on other sites is intentionally there for providing the
signed-in state in a third-party context.&lt;/p&gt;
&lt;h2 id=&quot;explicitly-state-cookie-usage-with-the-samesite-attribute&quot;&gt;Explicitly state cookie usage with the &lt;code&gt;SameSite&lt;/code&gt; attribute &lt;a class=&quot;w-headline-link&quot; href=&quot;#explicitly-state-cookie-usage-with-the-samesite-attribute&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The introduction of the &lt;code&gt;SameSite&lt;/code&gt; attribute (defined in
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00&quot;&gt;RFC6265bis&lt;/a&gt;)
allows you to declare if your cookie should be restricted to a first-party or
same-site context. It&#39;s helpful to understand exactly what &#39;site&#39; means here.
The site is the combination of the domain suffix and the part of the domain just
before it. For example, the &lt;code&gt;www.web.dev&lt;/code&gt; domain is part of the &lt;code&gt;web.dev&lt;/code&gt; site.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--key-term&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key Term:&lt;/strong&gt;  If the user is on &lt;code&gt;www.web.dev&lt;/code&gt; and requests an image
from &lt;code&gt;static.web.dev&lt;/code&gt; then that is a &lt;strong&gt;same-site&lt;/strong&gt; request.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://publicsuffix.org/&quot;&gt;public suffix list&lt;/a&gt; defines this, so it&#39;s not
just top-level domains like &lt;code&gt;.com&lt;/code&gt; but also includes services like &lt;code&gt;github.io&lt;/code&gt;.
That enables &lt;code&gt;your-project.github.io&lt;/code&gt; and &lt;code&gt;my-project.github.io&lt;/code&gt; to count as
separate sites.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--key-term&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key Term:&lt;/strong&gt;  If the user is on &lt;code&gt;your-project.github.io&lt;/code&gt; and requests
an image from &lt;code&gt;my-project.github.io&lt;/code&gt; that&#39;s a &lt;strong&gt;cross-site&lt;/strong&gt; request.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Introducing the &lt;code&gt;SameSite&lt;/code&gt; attribute on a cookie provides three different ways
to control this behaviour. You can choose to not specify the attribute, or you
can use &lt;code&gt;Strict&lt;/code&gt; or &lt;code&gt;Lax&lt;/code&gt; to limit the cookie to same-site requests.&lt;/p&gt;
&lt;p&gt;If you set &lt;code&gt;SameSite=Strict&lt;/code&gt; this means your cookie will only be sent in a
first-party context. In user terms, the cookie will only be sent if the site for
the cookie matches the site currently shown in the browser&#39;s URL bar. So, if the
&lt;code&gt;promo_shown&lt;/code&gt; cookie is set as follows:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: promo_shown=1; SameSite=Strict&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the user is on your site, then the cookie will be sent with the request as
expected. However when following a link into your site, say from another site or
via an email from a friend, on that initial request the cookie will not be sent.
This is good where you have cookies relating to functionality that will always
be behind an initial navigation, such as changing a password or making a
purchase, but is too restrictive for &lt;code&gt;promo_shown&lt;/code&gt;. If your reader follows the
link into the site, they want the cookie sent so their preference can be
applied.&lt;/p&gt;
&lt;p&gt;That&#39;s where &lt;code&gt;SameSite=Lax&lt;/code&gt; comes in by allowing the cookie to be sent with
these top-level navigations. Let&#39;s revisit the cat article example from above
where another site is referencing your content. They make use of your photo of
the cat directly and provide a link through to your original article.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Look at this amazing cat!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://blog.example/blog/img/amazing-cat.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Read the &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://blog.example/blog/cat.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;article&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the cookie has been set as so:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: promo_shown=1; SameSite=Lax&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the reader is on the other person&#39;s blog the cookie &lt;strong&gt;will not be sent&lt;/strong&gt;
when the browser requests &lt;code&gt;amazing-cat.png&lt;/code&gt;. However when the reader follows the
link through to &lt;code&gt;cat.html&lt;/code&gt; on your blog, that request &lt;strong&gt;will include&lt;/strong&gt; the
cookie. This makes &lt;code&gt;Lax&lt;/code&gt; a good choice for cookies affecting the display of the
site with &lt;code&gt;Strict&lt;/code&gt; being useful for cookies related to actions your user is
taking.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;  Neither &lt;code&gt;Strict&lt;/code&gt; nor &lt;code&gt;Lax&lt;/code&gt; are a silver bullet for your
site security. Cookies are sent as part of the user&#39;s request and you should
treat them the same as any other user input. That means sanitizing and
validating the input. Never use a cookie to store data you consider a
server-side secret.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Finally there is the option of not specifying the value which has previously
been the way of implicitly stating that you want the cookie to be sent in all
contexts. In the latest draft of
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03&quot;&gt;RFC6265bis&lt;/a&gt; this
is being made explicit by introducing a new value of &lt;code&gt;SameSite=None&lt;/code&gt;. This means
you can use &lt;code&gt;None&lt;/code&gt; to clearly communicate you intentionally want the cookie sent
in a third-party context.&lt;/p&gt;
&lt;figure class=&quot;w-figure  w-figure--center&quot;&gt;
  &lt;img src=&quot;https://web.dev/samesite-cookies-explained/samesite-none-lax-strict.png&quot; alt=&quot;Three cookies labelled None,
    Lax, or Strict depending on their context&quot; style=&quot;max-width: 60vw;&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption&quot;&gt;
    Explicitly mark the context of a cookie as &lt;tt&gt;None&lt;/tt&gt;, &lt;tt&gt;Lax&lt;/tt&gt;, or &lt;tt&gt;Strict&lt;/tt&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;div class=&quot;w-aside w-aside--objective&quot;&gt;
&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt;  If you provide a service that other sites consume such
as widgets, embedded content, affiliate programmes, advertising, or sign-in
across multiple sites then you should use &lt;code&gt;None&lt;/code&gt; to ensure your intent is clear.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;changes-to-the-default-behavior-without-samesite&quot;&gt;Changes to the default behavior without SameSite &lt;a class=&quot;w-headline-link&quot; href=&quot;#changes-to-the-default-behavior-without-samesite&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://chromestatus.com/features/schedule&quot;&gt;Chrome 76&lt;/a&gt; introduces a new
&lt;code&gt;same-site-by-default-cookies&lt;/code&gt; flag. Setting this flag will shift the default
treatment of cookies to apply &lt;code&gt;SameSite=Lax&lt;/code&gt; if no other &lt;code&gt;SameSite&lt;/code&gt; value is
provided. This is a move towards providing a more secure default and one that
makes the intended purpose of cookies clearer to users. If you want to delve
into the details, check out Mike West&#39;s
&lt;a href=&quot;https://tools.ietf.org/html/draft-west-cookie-incrementalism-00&quot;&gt;&amp;quot;Incrementally Better Cookies&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With this flag enabled in Chrome, both &lt;strong&gt;new and existing cookies&lt;/strong&gt; without the
&lt;code&gt;SameSite&lt;/code&gt; attribute will be restricted to the same site the user is browsing.
If you have cookies that need to be available in a third-party context, then you
must declare that to the browser and the user by marking them as
&lt;code&gt;SameSite=None&lt;/code&gt;. You will want to apply this when setting new cookies and
actively refresh existing cookies even if they are not approaching their expiry
date.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;If you rely on any services that provide third-party content
on your site, you should also check with the provider that they are updating
their services. You may need to update your dependencies or snippets to ensure
that your site picks up the new behavior.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;These changes are backwards-compatible with browsers that have correctly
implemented earlier versions of the &lt;code&gt;SameSite&lt;/code&gt; attribute, or just do not support
it at all. By applying these changes to your cookies, you are making their
intended use explicit rather than relying on the default behavior of the
browser. Likewise, any clients that do not recognize &lt;code&gt;SameSite=None&lt;/code&gt; as of yet
should ignore it and carry on as if the attribute was not set.&lt;/p&gt;
&lt;p&gt;Additionally in Chrome, if you also enable the
&lt;code&gt;cookies-without-same-site-must-be-secure&lt;/code&gt; flag then you must also specify
&lt;code&gt;SameSite=None&lt;/code&gt; cookies as &lt;code&gt;Secure&lt;/code&gt; or they will be rejected. Note, this flag
won&#39;t have any effect unless you also have &lt;code&gt;same-site-by-default-cookies&lt;/code&gt;
enabled.&lt;/p&gt;
&lt;div class=&quot;w-aside w-aside--warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt;  At the time of writing, the network library on iOS and Mac
incorrectly handles unknown &lt;code&gt;SameSite&lt;/code&gt; values and will &lt;strong&gt;treat any unknown
value&lt;/strong&gt; (including &lt;code&gt;None&lt;/code&gt;) as if it was &lt;code&gt;SameSite=Strict&lt;/code&gt;, which affects Safari
on Mac and browsers wrapping WebKit on iOS (Safari, Chrome, Firefox, and
others). This should be fixed in an upcoming release and may be available in the
Tech Preview now. You can track their progress in the
&lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=198181&quot;&gt;WebKit Bugzilla #198181&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;behavior-with-same-site-by-default-cookies-enabled&quot;&gt;Behavior with &lt;code&gt;same-site-by-default-cookies&lt;/code&gt; enabled &lt;a class=&quot;w-headline-link&quot; href=&quot;#behavior-with-same-site-by-default-cookies-enabled&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: promo_shown=1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--worse&quot;&gt;
    No attribute set
  &lt;/span&gt;
  &lt;p&gt;—  If you send a cookie without any
  &lt;code&gt;SameSite&lt;/code&gt; attribute specified.&lt;/p&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: promo_shown=1; SameSite=Lax&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--better&quot;&gt;
    Default behavior applied
  &lt;/span&gt;
  &lt;p&gt;—  Chrome will treat that cookie
  as if &lt;code&gt;SameSite=Lax&lt;/code&gt; was specified.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;behavior-with-cookies-without-same-site-must-be-secure-enabled&quot;&gt;Behavior with &lt;code&gt;cookies-without-same-site-must-be-secure&lt;/code&gt; enabled &lt;a class=&quot;w-headline-link&quot; href=&quot;#behavior-with-cookies-without-same-site-must-be-secure-enabled&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: widget_session=abc123; SameSite=None&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--worse&quot;&gt;
    Rejected
  &lt;/span&gt;
  &lt;p&gt;—  Setting a cookie without &lt;code&gt;Secure&lt;/code&gt; &lt;strong&gt;will be
  rejected&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;Set-Cookie: widget_session=abc123; SameSite=None; Secure&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-compare&quot;&gt;
  &lt;span class=&quot;w-compare__label w-compare__label--better&quot;&gt;
    Accepted
  &lt;/span&gt;
  &lt;p&gt;—  You must ensure that you pair &lt;code&gt;SameSite=None&lt;/code&gt;
  with the &lt;code&gt;Secure&lt;/code&gt; attribute.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;w-aside w-aside--objective&quot;&gt;
&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt;  These flags are intended to encourage more secure
defaults, but you shouldn&#39;t rely on a browser&#39;s default behavior. Best practice
is to always be explicit in stating the attributes so your intent is clear.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;designing-for-samesite-cookies&quot;&gt;Designing for &lt;code&gt;SameSite&lt;/code&gt; cookies &lt;a class=&quot;w-headline-link&quot; href=&quot;#designing-for-samesite-cookies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A core practice in software design is the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_concerns&quot;&gt;Separation of Concerns&lt;/a&gt;
which essentially states that each part of your system should have one clear
purpose. This is more simply put in the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_philosophy&quot;&gt;Unix philosophy&lt;/a&gt; which says,
&amp;quot;Make each program do one thing well&amp;quot;. By adding the &lt;code&gt;SameSite&lt;/code&gt; attribute to
your cookies you are creating the distinction between first-party and
third-party usage, but a third-party cookie can still be used in a first-party
context. It might seem simpler to just have the single cookie, but now you have
one component doing two jobs.&lt;/p&gt;
&lt;p&gt;If you have one cookie that&#39;s providing functionality in both contexts, for
example perhaps providing a user identifier for your main site and for an
embeddable widget, then consider separating these into separate cookies. As
browsers and users start to change how they accept and manage cookies providing
this distinction makes your site more robust in scenarios where third-party
cookies are blocked or cleared.&lt;/p&gt;
&lt;h2 id=&quot;what-should-i-do-to-enable-samesite-today&quot;&gt;What should I do to enable &lt;code&gt;SameSite&lt;/code&gt; today? &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-should-i-do-to-enable-samesite-today&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The majority of languages and libraries support the &lt;code&gt;SameSite&lt;/code&gt; attribute for
cookies, however the addition of &lt;code&gt;SameSite=None&lt;/code&gt; is still relatively new which
means that you may need to work around some of the standard behavior for now.
These are documented in the
&lt;a href=&quot;https://github.com/GoogleChromeLabs/samesite-examples&quot;&gt;&lt;code&gt;SameSite&lt;/code&gt; examples repo on GitHub&lt;/a&gt;.
If your particular use case is missing, please raise issue or submit a pull
request with your solution.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Kind thanks for contributions and feedback from Lily Chen, Malte Ubl, Mike
West, Rob Dodson, Tom Steiner, and Vivek Sekhar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cookie hero image by
&lt;a href=&quot;https://unsplash.com/photos/UiP3uF5JRWM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Pille-Riin Priske&lt;/a&gt;
on
&lt;a href=&quot;https://unsplash.com/?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Visual searching with the Web Perception Toolkit</title>
    <link href="https://web.dev/perception-toolkit/"/>
    <updated>2019-05-06T17:00:00-07:00</updated>
    <id>https://web.dev/perception-toolkit/</id>
    <content type="html">&lt;p&gt;Wouldn&#39;t it be great if users could search your site using their camera? Imagine
this. Your site is Razor McShaveyface. Your customers tell you they have trouble
finding the right cartridges for their razor when they reorder. They don&#39;t know
the right keywords for your product search. And let&#39;s be honest, they probably
never will.&lt;/p&gt;
&lt;p&gt;What if they never need to? What if they could point their phone&#39;s camera at the
UPC code on package and your site could present them with the right cartridge
and a big red &amp;quot;reorder&amp;quot; button?&lt;/p&gt;
&lt;p&gt;Think of other ways you can use a camera on a site. Imagine a site that
supports in-store price checking. Imagine getting information about a museum
exhibit or historical marker. Imagine identifying real-world landmarks in games
like geocaching or scavenger hunts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/perception-toolkit&quot;&gt;The Web Perception Toolkit&lt;/a&gt;
makes these camera-based scenarios possible. In some cases you can even create
an experience without writing code.&lt;/p&gt;
&lt;h2 id=&quot;how-does-it-work&quot;&gt;How does it work? &lt;a class=&quot;w-headline-link&quot; href=&quot;#how-does-it-work&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The open-source Web Perception Toolkit helps you add visual search to your
website. It passes a device camera stream through a set of detectors that map
real-world objects, here called &amp;quot;targets&amp;quot;, to content on your site. This mapping is defined
using Structured Data (JSON-LD) on your site. With this data, you can present the right information in a customizable
UI.&lt;/p&gt;
&lt;p&gt;I&#39;ll show you enough of this to give you a taste of how it works. For a complete
explanation, check out the &lt;a href=&quot;https://perceptiontoolkit.dev/getting-started/&quot;&gt;Getting
Started&lt;/a&gt; guide, the &lt;a href=&quot;https://perceptiontoolkit.dev/documentation/&quot;&gt;toolkit
reference&lt;/a&gt;, the &lt;a href=&quot;https://io.perceptiontoolkit.dev/&quot;&gt;I/O Sandbox demo&lt;/a&gt; and the &lt;a href=&quot;https://github.com/GoogleChromeLabs/perception-toolkit/tree/restructure_demo/demo&quot;&gt;sample demos&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;structured-data&quot;&gt;Structured data &lt;a class=&quot;w-headline-link&quot; href=&quot;#structured-data&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The toolkit can&#39;t find just any target in the camera&#39;s view. You must provide it
with linked JSON data for the targets you want it to recognize. This data also
contains information about those targets that will be shown to the user.&lt;/p&gt;
&lt;p&gt;The data is all you need to create a user experience like the one in the image
below. If you do nothing else, the Web Perception Toolkit can identify targets,
then show and hide cards based on the information provided in the data. Try this
for yourself using our &lt;a href=&quot;https://github.com/GoogleChromeLabs/perception-toolkit/tree/master/demo/artifact-map&quot;&gt;artifact-map
demo&lt;/a&gt;.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/perception-toolkit/found-target.png&quot; alt=&quot;The default interface is available by using just the linked data.&quot; width=&quot;300&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--center&quot;&gt;
    The default interface.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Add data to your site with a JSON linked data file, included using a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;
tag and the &lt;code&gt;&amp;quot;application/ld+json&amp;quot;&lt;/code&gt; MIME type.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;application/ld+json&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//path/to/your/sitemap.jsonld&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file itself looks something like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;@context&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://schema.googleapis.com/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;@type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ARArtifact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;arTarget&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;@type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Barcode&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;012345678912&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;arContent&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;@type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WebPage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/demo/artifact-map/products/product1.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Product 1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;This is a product with a barcode&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/demo/artifact-map/products/product1.png&quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-user-experience&quot;&gt;The user experience &lt;a class=&quot;w-headline-link&quot; href=&quot;#the-user-experience&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What if you want more than the default user experience? The toolkit gives you
livecycle events, Card and Button objects for crafting the user experience
around those events, and an easy way to style the cards. I&#39;m going to show a
little of this with code based losely on our &lt;a href=&quot;https://perceptiontoolkit.dev/getting-started/&quot;&gt;Getting
Started&lt;/a&gt; guide.&lt;/p&gt;
&lt;p&gt;The most important lifecycle event is &lt;code&gt;PerceivedResults&lt;/code&gt;, which is firedß every
time a target is found. A target can be a real-world object or a marker such as
a bar code or QR code.&lt;/p&gt;
&lt;p&gt;The process for responding to this event is the same as for any other event with
an exception alrady alluded to. If you don&#39;t implement the event, a user
interface is automatically created using structured data. To override this
behavior start your event handler by calling&lt;code&gt;event.preventDefault()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.container&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onPerceivedResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// preventDefault() to stop default result Card from showing.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Process the event.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PerceptionToolkit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PerceivedResults&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onPerceivedResults&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s look at the event more closely. The event itself contains arrays of
markers and targets that it has both found and lost. When targets are found in
the world, the even fires and passes found objects in &lt;code&gt;event.found&lt;/code&gt;. Similarly,
when targets pass from the camera view the event fires again, passing lost
objects in &lt;code&gt;event.lost&lt;/code&gt;. This helps account for hand and marker movements:
cameras not held steadily enough, dropped markers, that kind of thing.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onPerceivedResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// preventDefault() to stop default result Card from showing&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;childNodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; found&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lost &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Deal with lost and found objects.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you show an appropriate card based on what the toolkit found.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onPerceivedResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;childNodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; found&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lost &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;found&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; lost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Object not found.&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Show a card with an offer to show the catalog.&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;found&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Object found.&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Show a card with a reorder button.&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/mark&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding cards and buttons is simply a matter of instantiating them and appending
them to a parent object. For example:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Card &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PerceptionToolkit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Elements&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; card &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Your message here.&#39;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, here&#39;s what the whole thing looks like. Notice the conveniences I&#39;ve
added to the user experience. Whether the marker is found or not, I provide
one-click access to what I think is most useful in the circumstances.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onPerceivedResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// preventDefault() to stop default result Card from showing&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;childNodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; found&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lost &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ActionButton&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Card &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PerceptionToolkit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Elements&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;found&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; lost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;//Make a view catalog button.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; button &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ActionButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    button&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;View catalog&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    button&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;//Run code to launch a catalog.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;//Make a card for the button.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; card &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;We wish we could help, but that\&#39;s not our razor. Would you like to see our catalog?&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;//Tell the toolkit it does not keep the card around&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// if it finds something it recognizes.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notRecognized &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;found&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;//Make a reorder button.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; button &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ActionButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    button&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Reorder&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    botton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;//Run code to reorder.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; card &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; found&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    card&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;formatting-cards&quot;&gt;Formatting cards &lt;a class=&quot;w-headline-link&quot; href=&quot;#formatting-cards&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Web Perception Toolkit provides built-in formatting for cards and buttons
with the default stylesheet. But you can easily add your own. The provided
&lt;code&gt;Card&lt;/code&gt; and &lt;code&gt;ActionButton&lt;/code&gt; objects contain &lt;code&gt;style&lt;/code&gt; properties (among many others)
that let you put your organizational stamp on the look and feel. To include the
default stylesheet, add a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element to your page.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//path/to/toolkit/styles/perception-toolkit.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;w-headline-link&quot; href=&quot;#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I said at the top, this is not an exhaustive look at the &lt;a href=&quot;https://github.com/GoogleChromeLabs/perception-toolkit&quot;&gt;Web Perception
Toolkit&lt;/a&gt;. Hopefully it gives
you a sense of how easy it is to add visual searching to a website. Learn more
with the &lt;a href=&quot;https://perceptiontoolkit.dev/getting-started/&quot;&gt;Getting Started&lt;/a&gt; guide
and the &lt;a href=&quot;https://github.com/GoogleChromeLabs/perception-toolkit/tree/restructure_demo/demo&quot;&gt;sample
demos&lt;/a&gt;.
Dig in to the &lt;a href=&quot;https://perceptiontoolkit.dev/documentation/&quot;&gt;toolkit
documentation&lt;/a&gt; to learn what it
can do.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Hands-on with Portals: seamless navigation on the Web</title>
    <link href="https://web.dev/hands-on-portals/"/>
    <updated>2019-05-05T17:00:00-07:00</updated>
    <id>https://web.dev/hands-on-portals/</id>
    <content type="html">&lt;p&gt;Making sure your pages load fast is key to delivering a good user experience.
But one area we often overlook is page transitions—what our users see when
they move between pages.&lt;/p&gt;
&lt;p&gt;A new web platform API called &lt;a href=&quot;https://github.com/WICG/portals&quot;&gt;Portals&lt;/a&gt; aims to
help with this by streamlining the experience as users navigate &lt;em&gt;across&lt;/em&gt; your
site. See Portals in action:&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--fullbleed&quot;&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; class=&quot;w-screenshot&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/portals_vp9.webm&quot; type=&quot;video/webm; codecs=vp8&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/portals_h264.mp4&quot; type=&quot;video/mp4; codecs=h264&quot;&gt;
  &lt;/video&gt;
 &lt;figcaption class=&quot;w-figcaption w-figcaption--fullbleed&quot;&gt;
    Seamless embeds and navigation with Portals. Created by &lt;a href=&quot;https://twitter.com/argyleink&quot;&gt;Adam Argyle&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;what-portals-enable&quot;&gt;What Portals enable &lt;a class=&quot;w-headline-link&quot; href=&quot;#what-portals-enable&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Single Page Applications (SPAs) offer nice transitions
but come at the cost of higher complexity to build.
Multi-page Applications (MPAs) are much easier to build,
but you end up with blank screens between pages.&lt;/p&gt;
&lt;p&gt;Portals offer the best of both worlds:
the low complexity of an MPA with the seamless transitions of an SPA.
Think of them like an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; in that they allow for embedding,
but unlike an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;,
they also come with features to navigate to their content.&lt;/p&gt;
&lt;p&gt;Seeing is believing:
please first check out what we showcased at Chrome Dev Summit last year:&lt;/p&gt;
&lt;div style=&quot;width:100%; padding-top: 56.25%; position: relative;&quot;&gt;
  &lt;iframe style=&quot;width:100%; height: 100%;position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);&quot; src=&quot;https://www.youtube.com/embed/Ai4aZ9Jbsys?start=1081&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;With classic navigations, users have to wait with a blank screen
until the browser finishes rendering the destination.
With Portals, users get to experience an animation,
while the &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; pre-renders content and creates a seamless navigation experience.&lt;/p&gt;
&lt;p&gt;Before Portals, we could have rendered another page using an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;. We could also have added animations to move the frame around the page. But an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; won&#39;t let you navigate into its content. Portals close this gap, enabling interesting use cases.&lt;/p&gt;
&lt;h2 id=&quot;try-out-portals-in-chrome-canary&quot;&gt;Try out Portals in Chrome Canary &lt;a class=&quot;w-headline-link&quot; href=&quot;#try-out-portals-in-chrome-canary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Try out Portals in Chrome Canary by flipping an experimental flag:
&lt;code&gt;chrome://flags/#enable-portals&lt;/code&gt;.
Once Portals are enabled, confirm in DevTools that you have the new shiny &lt;code&gt;HTMLPortalElement&lt;/code&gt;.&lt;/p&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/hands-on-portals/HTMLPortalElement.png&quot; alt=&quot;A screenshot of the DevTools console showing the HTMLPortalElement&quot;&gt;
&lt;p&gt;Let&#39;s walk through a basic example.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create a portal with the wikipedia page, and embed it&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// (like an iframe). You can also use the &amp;lt;portal&gt; tag instead.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https://en.wikipedia.org/wiki/World_Wide_Web&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// When the user touches the preview (embedded portal):&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// do fancy animation, e.g. expand …&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// and finish by doing the actual transition&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s that simple. Try this code in the DevTools console, the wikipedia page should open up.&lt;/p&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/hands-on-portals/portal-preview-demo.gif&quot; alt=&quot;A gif of preview portal style demo&quot;&gt;
&lt;p&gt;If you wanted to build something like we showed at Chrome Dev Summit which works just like the demo above,
the following snippet will be of interest.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Adding some styles with transitions&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; initialScale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  portal {&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    position:fixed;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    width: 100%;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    height: 100%;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    opacity: 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    box-shadow: 0 0 20px 10px #999;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    transform: scale(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;initialScale&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    bottom: calc(20px + 50% * &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;initialScale&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - 50%);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    left: calc(20px + 50% * &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;initialScale&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - 50%);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    z-index: 10000;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  }&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  .portal-transition {&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    transition:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      transform 0.4s,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      bottom 0.7s,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      left 0.7s,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      opacity 1.0s;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  }&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  @media (prefers-reduced-motion: reduce) {&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    .portal-transition {&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      transition: all 0.001s;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    }&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  }&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  .portal-reveal {&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    transform: scale(1.0);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    bottom: 0px;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    left: 0px;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  }&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  .fade-in {&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    opacity: 1.0;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  }&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; portal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Let&#39;s navigate into the WICG Portals spec page&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https://wicg.github.io/portals/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Add a class that defines the transition. Consider using &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// `prefers-reduced-motion` media query to control the animation. &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// https://developers.google.com/web/updates/2019/03/prefers-reduced-motion&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portal-transition&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Animate the portal once user interacts&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portal-reveal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;transitionend&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;propertyName &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;bottom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Activate the portal once the transition has completed&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; portal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Waiting for the page to load.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// using setTimeout is a suboptimal way and it&#39;s best to fade-in&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// when receiving a load complete message from the portal via postMessage&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fade-in&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also easy to do feature detection to progressively enhance a website using Portals.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;HTMLPortalElement&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// If this is a platform that have Portals...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; portal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to quickly experience what Portals feel like, try using
&lt;a href=&quot;https://uskay-portals-demo.glitch.me/&quot;&gt;uskay-portals-demo.glitch.me&lt;/a&gt;.
Be sure you access it with Chrome Canary and turn on the experimental flag!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enter a URL you want to preview.&lt;/li&gt;
&lt;li&gt;The page will then be embedded as a &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;Click on the preview.&lt;/li&gt;
&lt;li&gt;The preview will be activated after an animation.&lt;/li&gt;
&lt;/ol&gt;
&lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/hands-on-portals/glitch.gif&quot; alt=&quot;A gif of using the glitch demo of using Portals&quot;&gt;
&lt;h2 id=&quot;check-out-the-spec&quot;&gt;Check out the spec &lt;a class=&quot;w-headline-link&quot; href=&quot;#check-out-the-spec&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We are actively discussing
&lt;a href=&quot;https://wicg.github.io/portals/&quot;&gt;the Portals spec&lt;/a&gt; in the Web Incubation Community Group (WICG).
To quickly get up to speed, take a look at
&lt;a href=&quot;https://github.com/WICG/portals/blob/master/explainer.md&quot;&gt;the explainer&lt;/a&gt;.
These are the three important features to familiarize yourself with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wicg.github.io/portals/#the-portal-element&quot;&gt;The &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element:&lt;/a&gt; The HTML element itself. The API is very simple. It consists of the &lt;code&gt;src&lt;/code&gt; attribute, the &lt;code&gt;activate&lt;/code&gt; function and an interface for messaging (&lt;code&gt;postMessage&lt;/code&gt;). &lt;code&gt;activate&lt;/code&gt; takes an optional argument to pass data to the &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; upon activation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wicg.github.io/portals/#the-portalhost-interface&quot;&gt;The &lt;code&gt;portalHost&lt;/code&gt; interface:&lt;/a&gt; Adds a &lt;code&gt;portalHost&lt;/code&gt; object to the &lt;code&gt;window&lt;/code&gt; object. This lets you check if the page is embedded as a &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element. It also provides an interface for messaging (&lt;code&gt;postMessage&lt;/code&gt;) back to the host.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wicg.github.io/portals/#the-portalactivateevent-interface&quot;&gt;The PortalActivateEvent interface:&lt;/a&gt; An event that fires when the &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; is activated. There is a neat function called &lt;code&gt;adoptPredecessor&lt;/code&gt; which you can use to retrieve the previous page as a &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element. This allows you to create seamless navigations and composed experiences between two pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s look beyond the basic usage pattern. Here is a non-exhaustive list of what you can achieve with Portals along with sample code.&lt;/p&gt;
&lt;h3 id=&quot;customize-the-style-when-embedded-as-a-lessportalgreater-element&quot;&gt;Customize the style when embedded as a &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element &lt;a class=&quot;w-headline-link&quot; href=&quot;#customize-the-style-when-embedded-as-a-lessportalgreater-element&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Detect whether this page is hosted in a portal&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;portalHost&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Customize the UI when being embedded as a portal&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;messaging-between-the-lessportalgreater-element-and-portalhost&quot;&gt;Messaging between the &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element and &lt;code&gt;portalHost&lt;/code&gt; &lt;a class=&quot;w-headline-link&quot; href=&quot;#messaging-between-the-lessportalgreater-element-and-portalhost&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Send message to the portal element&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; portal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;someKey&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; someValue&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ORIGIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Receive message via window.portalHost&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;portalHost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;someKey&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// handle the event&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;activating-the-lessportalgreater-element-and-receiving-the-portalactivate-event&quot;&gt;Activating the &lt;code&gt;&amp;lt;portal&amp;gt;&lt;/code&gt; element and receiving the &lt;code&gt;portalactivate&lt;/code&gt; event &lt;a class=&quot;w-headline-link&quot; href=&quot;#activating-the-lessportalgreater-element-and-receiving-the-portalactivate-event&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// You can optionally add data to the argument of the activate function&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;somekey&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;somevalue&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// The portal content will receive the portalactivate event&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// when the activate happens&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portalactivate&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Data available as evt.data&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;retrieving-the-predecessor&quot;&gt;Retrieving the predecessor &lt;a class=&quot;w-headline-link&quot; href=&quot;#retrieving-the-predecessor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Listen to the portalactivate event&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;portalactivate&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// ... and creatively use the predecessor&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; portal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;adoptPredecessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;someElm&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;knowing-your-page-was-adopted-as-a-predecessor&quot;&gt;Knowing your page was adopted as a predecessor &lt;a class=&quot;w-headline-link&quot; href=&quot;#knowing-your-page-was-adopted-as-a-predecessor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// The activate function returns a Promise.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// When the promise resolves, it means that the portal has been activated.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// If this document was adopted by it, then window.portalHost will exist.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;portal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Check if this document was adopted into a portal element.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;portalHost&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// You can start communicating with the portal element&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// i.e. listen to messages&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;portalHost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;// handle the event&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By combining all of the features supported by Portals,
you can build really fancy user experiences.
For instance, the demo below demonstrates how Portals can enable a seamless user experience
between a website and third party embed content.&lt;/p&gt;
&lt;div style=&quot;width:100%; padding-top: 56.25%; position: relative;&quot;&gt;
  &lt;iframe style=&quot;width:100%; height: 100%;position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);&quot; src=&quot;https://www.youtube.com/embed/4JkipxFVE9k&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;Interested in this demo?
&lt;a href=&quot;https://github.com/WICG/portals/tree/master/demos/portal-embed-demo&quot;&gt;Fork it on GitHub&lt;/a&gt;
and build your own version!&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;use-cases-and-plans&quot;&gt;Use cases and plans &lt;a class=&quot;w-headline-link&quot; href=&quot;#use-cases-and-plans&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We hope you liked this brief tour of Portals! We can&#39;t wait to see what you can come up with. For instance, you might want to start using Portals for non-trivial navigations such as: pre-rendering the page for your best-seller product from a product category listing page.&lt;/p&gt;
&lt;p&gt;Another important thing to know is that Portals can be used in cross-origin navigations, just like an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;. So, if you have multiple websites that cross reference one another, you can also use Portals to create seamless navigations between two different websites. This cross-origin use case is very unique to Portals, and can even improve the user experience of SPAs.&lt;/p&gt;
&lt;h2 id=&quot;feedback-welcome&quot;&gt;Feedback welcome &lt;a class=&quot;w-headline-link&quot; href=&quot;#feedback-welcome&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Portals are still in the early stages so not everything is working yet (that&#39;s why it&#39;s behind an experimental flag). That said, it&#39;s ready for experimentation in Chrome Canary. Feedback from the community is crucial to the design of new APIs, so please try it out and tell us what you think! You can check the current limitations on &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=957836&quot;&gt;the Chromium bug tracker&lt;/a&gt; and if you have any feature requests, or feedback, please head over to the &lt;a href=&quot;https://github.com/WICG/portals/issues&quot;&gt;WICG GitHub repo&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>How to report metrics and build a performance culture</title>
    <link href="https://web.dev/how-to-report-metrics/"/>
    <updated>2019-05-04T17:00:00-07:00</updated>
    <id>https://web.dev/how-to-report-metrics/</id>
    <content type="html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;w-headline-link&quot; href=&quot;#introduction&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Website performance is a key goal towards e-commerce conversions, so it must be
a core priority for business stakeholders and leadership, not just development
teams.&lt;/p&gt;
&lt;p&gt;For that reason, it&#39;s crucial to make performance metrics visible and tangible
for all stakeholders, and to build reporting and monitoring into your
workflow.&lt;/p&gt;
&lt;p&gt;This guide describes what to be aware of and what to avoid to achieve
this successfully.&lt;/p&gt;
&lt;h2 id=&quot;a-word-about-metrics&quot;&gt;A word about metrics &lt;a class=&quot;w-headline-link&quot; href=&quot;#a-word-about-metrics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are countless metrics to evaluate website performance, and while it may
seem useful to collect all of them, too many metrics can be confusing and
misleading. There are several ways to deal with this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gather multiple metrics and try to narrow and filter afterwards on
what might be relevant for the task at hand.&lt;/li&gt;
&lt;li&gt;Abstract metrics into an overall score, as for example
&lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/v3/scoring#perf&quot;&gt;Lighthouse does&lt;/a&gt;.
This can be especially useful for non-technical staff and other
stakeholders, but is probably insufficient for deeper technical analysis.&lt;/li&gt;
&lt;li&gt;Try to find the one metric which is most relevant as a predictor for
your conversions and then optimize towards this.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, a pragmatic combination makes the most sense here. Gather more rather
than less to begin with, report an abstract score to management, and optimize
towards the one metric which best predicts your conversions.&lt;/p&gt;
&lt;p&gt;Finding this metric can be done by using your analytics tool of choice
to map performance metrics to user engagement, conversions and transaction values.
For example, a custom report to do so looks like this in Google Analytics:&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/how-to-report-metrics/speed_report.png&quot; alt=&quot;A Google Analytics dashboard shows a number of fields being added to a custom report.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--center&quot;&gt;
    Fig 1: Custom report in Google Analytics to analyze the impact of speed on conversions and engagement.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;div class=&quot;w-aside w-aside--caution&quot;&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;
&lt;strong&gt;Metrics can be deceiving.&lt;/strong&gt;
Unfortunately metrics can sometimes be misleading with respect to performance.
Keep reading for specifics.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;increasing-bounce-rate&quot;&gt;Increasing bounce rate &lt;a class=&quot;w-headline-link&quot; href=&quot;#increasing-bounce-rate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem&quot;&gt;Problem &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Often it is assumed that bounce rate will drop when page load speed
increases. While this normally does hold true, measurements sometimes show the
opposite. This is because analytics can only measure a bounce after the
analytics library is loaded. A faster page load means analytics code also loads
faster, so analytics may see more bounces even if there aren&#39;t more happening.&lt;/p&gt;
&lt;h3 id=&quot;solution&quot;&gt;Solution &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This can be eased by measuring
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#load_abandonment&quot;&gt;real page abandonment&lt;/a&gt; instead.&lt;/p&gt;
&lt;h2 id=&quot;decreasing-relative-conversions&quot;&gt;Decreasing relative conversions &lt;a class=&quot;w-headline-link&quot; href=&quot;#decreasing-relative-conversions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem-2&quot;&gt;Problem &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Relative conversions may sometimes seem to drop for faster sites.
This is because faster pages reach a bigger audience who might be less engaged
or committed. While incremental traffic and conversions increase with faster
pages, relative conversions (the ratio of conversions to page views or visitors)
might still drop.&lt;/p&gt;
&lt;h3 id=&quot;solution-2&quot;&gt;Solution &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This effect can be eased by looking out for absolute conversions instead, and
even calculate Cost Per Sales (conversions divided with investment level) or ROI.&lt;/p&gt;
&lt;h2 id=&quot;dropping-engagement&quot;&gt;Dropping engagement &lt;a class=&quot;w-headline-link&quot; href=&quot;#dropping-engagement&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem-3&quot;&gt;Problem &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem-3&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Page engagement may seem to drop for a faster page.&lt;/p&gt;
&lt;h3 id=&quot;solution-3&quot;&gt;Solution &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution-3&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This can actually be a positive signal! If the page is faster
users can reach their goal more quickly and might just have shorter sessions and
less time on page.&lt;/p&gt;
&lt;h2 id=&quot;using-averages-or-medians&quot;&gt;Using averages or medians &lt;a class=&quot;w-headline-link&quot; href=&quot;#using-averages-or-medians&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem-4&quot;&gt;Problem &lt;a class=&quot;w-headline-link&quot; href=&quot;#problem-4&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In general, it can be deceiving to look at averages and medians of
performance metrics (see this nicely described in
&lt;em&gt;&lt;a href=&quot;https://bravenewgeek.com/everything-you-know-about-latency-is-wrong/&quot;&gt;Everything You Know About Latency Is Wrong&lt;/a&gt;)&lt;/em&gt;.
Similar to a chain being just as strong as the weakest link, the performance of
a funnel is only as good as its slowest load. A single slow load may be enough
to lose the user. Therefore averages and medians are more likely to hide the
real performance issues, than to reveal them.&lt;/p&gt;
&lt;h3 id=&quot;solution-4&quot;&gt;Solution &lt;a class=&quot;w-headline-link&quot; href=&quot;#solution-4&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&#39;s best to analyze complete distributions, but as this is not
always easily possible we recommend using 90th percentiles—this is the value
for which 90% of loads were faster. Even then a user hitting ten pages will
still have one load slower than this, and might drop out there.&lt;/p&gt;
&lt;p&gt;While performance measurement is highly important, make sure to keep an open
mind and question unexpected results—and make sure to not report misleading
numbers to stakeholders and management. If unsure on what to pick and report,
we&#39;d advise as a minimum for 90th percentile
&lt;a href=&quot;https://web.dev/first-contentful-paint&quot;&gt;First Contentful Paint&lt;/a&gt;, which is also what we use
across our public tooling.&lt;/p&gt;
&lt;h2 id=&quot;third-party-content&quot;&gt;Third party content &lt;a class=&quot;w-headline-link&quot; href=&quot;#third-party-content&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Website performance is especially prone to being dragged down
by third party content
(see &lt;a href=&quot;https://web.dev/render-blocking-resources&quot;&gt;Eliminate render-blocking resources&lt;/a&gt;).
This is a particular problem for e-commerce, often due to trackers and widgets.&lt;/p&gt;
&lt;p&gt;Some ways to handle third party content with respect to performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always keep third party content out of the
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/&quot;&gt;critical rendering path&lt;/a&gt;.
If the third party has server problems and times out, it will impact your
website heavily. You can test and simulate this via
&lt;a href=&quot;https://css-tricks.com/use-webpagetest-api/#single-point-of-failure&quot;&gt;WebPageTest Single-Point-of-Failure&lt;/a&gt;
tests.&lt;/li&gt;
&lt;li&gt;Continually measure and report the ratio of third party content, for
example via WebPageTest &lt;a href=&quot;https://www.webpagetest.org/domains.php&quot;&gt;domain
breakdown&lt;/a&gt;. Make sure to use
performance budgets for third party content.&lt;/li&gt;
&lt;li&gt;If you suspect that a particular third party component is having a
detrimental effect on performance, do a performance comparison with it
included and excluded.
&lt;a href=&quot;https://andydavies.me/blog/2018/02/19/using-webpagetest-to-measure-the-impact-of-3rd-party-tags/&quot;&gt;Find out how&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Try to stay on one stack from one vendor if possible. For example, if
you have a tag manager and analytics on one stack, you may only need a
single script, and may be able to
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/http2/&quot;&gt;take advantage of HTTP2 synergies&lt;/a&gt;
as there is only one host involved.&lt;/li&gt;
&lt;li&gt;Make sure not to use the same functionality from two different vendors.
You shouldn&#39;t need two tag managers or two analytics platforms.&lt;/li&gt;
&lt;li&gt;Routinely audit and clean out redundant third party scripts, trackers
and widgets. This can easily be done via &lt;a href=&quot;https://chrome.google.com/webstore/detail/ghostery-%E2%80%93-privacy-ad-blo/mlomiejdfkolichcflejclcbmpeaniij?hl=en&quot;&gt;Ghostery Extension&lt;/a&gt; or tools like &lt;a href=&quot;https://chrome.google.com/webstore/detail/whatruns/cmkdbmfndkfgebldhnkbfhlneefdaaip?hl=en&quot;&gt;WhatRuns&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;w-figure w-figure--center&quot;&gt;
  &lt;img class=&quot;w-screenshot&quot; src=&quot;https://web.dev/how-to-report-metrics/ghostery.png&quot; alt=&quot;A Ghostery report showing all loaded trackers.&quot;&gt;
  &lt;figcaption class=&quot;w-figcaption w-figcaption--center&quot;&gt;
    A Ghostery report showing all loaded trackers
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Learn more in
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/&quot;&gt;Loading Third-Party JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;performance-culture&quot;&gt;Performance Culture &lt;a class=&quot;w-headline-link&quot; href=&quot;#performance-culture&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unfortunately performance is often seen as a one-off optimization task, and then
regresses over time as stakeholders raise new feature requests or insist on
adding new trackers and widgets.&lt;/p&gt;
&lt;p&gt;Performance must be a continuous goal to improve acquisition, discovery, and
conversion rates as well as safeguarding the reputation of your brand. This can
be achieved with &lt;a href=&quot;https://web.dev/performance-budgets-101&quot;&gt;performance
budgets&lt;/a&gt;
like &lt;a href=&quot;https://medium.com/@addyosmani/a-tinder-progressive-web-app-performance-case-study-78919d98ece0&quot;&gt;Tinder did&lt;/a&gt;,
and by establishing and fostering a performance culture, where all employees and
especially decision makers recognize speed as a core feature of the website.&lt;/p&gt;
&lt;h2 id=&quot;make-metrics-tangible&quot;&gt;Make metrics tangible &lt;a class=&quot;w-headline-link&quot; href=&quot;#make-metrics-tangible&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Reports on metrics are often abstract and easy to challenge or dismiss. It&#39;s
best to make your performance tangible and visible. There are several good ways
to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.theverge.com/2015/10/28/9625062/facebook-2g-tuesdays-slow-internet-developing-world&quot;&gt;Facebook&lt;/a&gt;
and Google do this by providing slow networks across the company for testing.&lt;/li&gt;
&lt;li&gt;Make average, low-spec devices with low
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/poor-connectivity/&quot;&gt;bandwidth or high latencies&lt;/a&gt; available
to management and other stakeholders.&lt;/li&gt;
&lt;li&gt;Consider adding overlays showing performance metrics on your development
or staging servers. Connections to these servers from mobile can
potentially be throttled by default on corporate networks.&lt;/li&gt;
&lt;li&gt;Monitors can be placed strategically across the company showing videos
or timestrips of your website&#39;s loading behavior, preferably in comparison
to competitors. WebPageTest &lt;a href=&quot;https://www.webpagetest.org/video/&quot;&gt;can create
these&lt;/a&gt; very easily and automatically.&lt;/li&gt;
&lt;li&gt;Performance can also be fun—maybe a &lt;a href=&quot;https://g.co/perfgame&quot;&gt;game based on an individual Lighthouse report&lt;/a&gt; can reach audiences which can&#39;t be reached by pure reports and metrics?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap &lt;a class=&quot;w-headline-link&quot; href=&quot;#recap&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide explains why the selection of metrics, and how they are reported and handled, is as important as the measurement and optimization itself—or even more. Make sure to prefer percentiles or distributions over averages, be cautious of using just bounce rate or relative conversion rate as impact measures, and make sure that metrics are easy to understand and tangible for stakeholders across the company. Establishing a performance culture is also an important step towards a well performing e-commerce site.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>web.dev at I/O 2019</title>
    <link href="https://web.dev/web-dev-io-2019/"/>
    <updated>2019-05-03T17:00:00-07:00</updated>
    <id>https://web.dev/web-dev-io-2019/</id>
    <content type="html">&lt;p&gt;With I/O right around the corner, we wanted to sneak out a quick update to let
you know what we&#39;ve been working on since our beta launch at Chrome Dev Summit.&lt;/p&gt;
&lt;h2 id=&quot;new-look&quot;&gt;New look &lt;a class=&quot;w-headline-link&quot; href=&quot;#new-look&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We want web.dev to be a best-in-class reading experience—something that folks
look forward to opening on their phones, tablets, and desktops.&lt;/p&gt;
&lt;p&gt;In this iteration, we launched a new design with improved typography and a bit
more visual punch. We have a number of UI features still in the backlog that we
plan to ship in the coming months, but we hope you like this new visual style 💅&lt;/p&gt;
&lt;h2 id=&quot;improved-lighthouse-support&quot;&gt;Improved Lighthouse support &lt;a class=&quot;w-headline-link&quot; href=&quot;#improved-lighthouse-support&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure class=&quot;w-figure w-figure--inline-right&quot;&gt;
  &lt;img src=&quot;https://web.dev/images/collections/lighthouse-performance.svg&quot; alt=&quot;&quot; width=&quot;200&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;In the Learn section, we&#39;ve rolled out
&lt;a href=&quot;https://web.dev/learn#lighthouse&quot;&gt;new Lighthouse audit docs&lt;/a&gt; with updated
images, scoring explainers, and links to guides which dive deeper into specific
topics.&lt;/p&gt;
&lt;p&gt;We&#39;ve also connected these docs to &lt;a href=&quot;https://web.dev/measure&quot;&gt;our measure tool&lt;/a&gt; so when you
audit your site you should have a better understanding of what each result
means and how to fix it.&lt;/p&gt;
&lt;h2 id=&quot;new-blog!&quot;&gt;New blog! &lt;a class=&quot;w-headline-link&quot; href=&quot;#new-blog!&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Last, but not least, we have our new blog.&lt;/p&gt;
&lt;p&gt;Not that long ago, our team used to produce a site called
&lt;a href=&quot;https://www.html5rocks.com/&quot;&gt;HTML5Rocks&lt;/a&gt;. HTML5Rocks always felt like a group of
folks coming together to share their collective excitement about what was
possible on the web.&lt;/p&gt;
&lt;figure class=&quot;w-figure w-figure--inline-right&quot;&gt;
  &lt;img src=&quot;https://web.dev/web-dev-io-2019/html5rocks.png&quot; alt=&quot;The HTML5Rocks logo.&quot; width=&quot;200&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;As we&#39;ve been building web.dev, we&#39;ve often said that we want to bring back some
of that &amp;quot;HTML5Rocks magic ✨&amp;quot;. Get people excited about what&#39;s possible, and use
new services like &lt;a href=&quot;https://glitch.com/&quot;&gt;Glitch&lt;/a&gt; to let them remix and play with
running code. So for the web.dev blog, we&#39;re making it our mission to keep
things open, experimental, and fun 👩‍🔬&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up &lt;a class=&quot;w-headline-link&quot; href=&quot;#wrapping-up&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to stay up to date with the latest on web.dev, you can click that
little subscribe button in the bottom-right corner to join our newsletter, or
add the site to your feed reader of choice.&lt;/p&gt;
&lt;p&gt;We&#39;re really excited for this year&#39;s I/O, so definitely check back in the days
and weeks ahead as we roll out new content. There&#39;s a boatload of stuff coming
and we can&#39;t wait to share it with you.&lt;/p&gt;
&lt;p&gt;Thanks! 👋&lt;/p&gt;
&lt;p&gt;&lt;em&gt;- The web.dev crew&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
  <entry>
    <title>Discover performance opportunities with Lighthouse</title>
    <link href="https://web.dev/discover-performance-opportunities-with-lighthouse/"/>
    <updated>2018-11-04T16:00:00-08:00</updated>
    <id>https://web.dev/discover-performance-opportunities-with-lighthouse/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt; is a tool that
helps you measure and find ways to improve a page&#39;s performance. Here&#39;s the
general workflow for how you use Lighthouse:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You tell Lighthouse what page to audit.&lt;/li&gt;
&lt;li&gt;Lighthouse loads that page and records how long the page takes to hit
various performance milestones. These milestones are called &lt;strong&gt;metrics&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Lighthouse gives you a report on how the page did. The report provides a
score for each metric and a list of &lt;strong&gt;opportunities&lt;/strong&gt; which, if you implement
them, should make the page load faster.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your mission is to improve your metrics scores over time, or at least make sure
that they don&#39;t get worse. There&#39;s no way to work on metrics directly, though.
Instead, you follow the opportunities that Lighthouse provides. Working on those
opportunities tends to improve your metrics scores.&lt;/p&gt;
&lt;h2 id=&quot;run-lighthouse-from-your-profile-page&quot;&gt;Run Lighthouse from your profile page &lt;a class=&quot;w-headline-link&quot; href=&quot;#run-lighthouse-from-your-profile-page&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Lighthouse from your &lt;a href=&quot;https://web.dev/measure&quot;&gt;web.dev profile&lt;/a&gt; page:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Provide any URL, and Lighthouse runs a series of audits generating a report of how well the page did.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Review the audits report to identify areas in which your page can be improved.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For each audit, you&#39;ll find guidance and immediate steps you can take to improve your scores.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;run-lighthouse-from-chrome-devtools&quot;&gt;Run Lighthouse from Chrome DevTools &lt;a class=&quot;w-headline-link&quot; href=&quot;#run-lighthouse-from-chrome-devtools&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chrome DevTools is the set of web developer tools that are built directly into
the Google Chrome browser. You don&#39;t have to download anything to get DevTools.
If you have Chrome, then you have DevTools.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Chrome, go to the page that you want to audit.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Control+Shift+J&lt;/code&gt; or &lt;code&gt;Command+Option+J&lt;/code&gt; (Mac) to open DevTools.&lt;/li&gt;
&lt;/ol&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-1.png&quot; alt=&quot;DevTools opened and docked to the right hand side of the screen.&quot;&gt;
&lt;p&gt;Click the &lt;strong&gt;Audits&lt;/strong&gt; tab. If you don&#39;t see this tab, click the » symbol
and then select &lt;strong&gt;Audits&lt;/strong&gt; from the list. Lighthouse is the
engine that powers the &lt;strong&gt;Audits&lt;/strong&gt; panel. That&#39;s why you see an image of a
lighthouse.&lt;/p&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-2.png&quot; alt=&quot;DevTools opened to the Lighthouse audits panel.&quot;&gt;
&lt;ol&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Mobile&lt;/strong&gt; radio button is selected. When Lighthouse
audits your page, it will simulate a mobile device&#39;s viewport and user
agent string.&lt;/li&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Performance&lt;/strong&gt; checkbox is enabled. You can enable or
disable the rest of the checkboxes in the &lt;strong&gt;Audits&lt;/strong&gt; section. If you enable
them, then you&#39;ll see a bunch of opportunities on ways to improve those
other aspects of your page.&lt;/li&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Simulated Fast 3G, 4x CPU Slowdown&lt;/strong&gt; radio button is
selected. Lighthouse doesn&#39;t actually throttle your network or CPU while it
loads the page. Instead, it looks at how long the page took to load under
normal conditions, and then it estimates how long it would have taken on a
fast 3G network with a CPU that is 4 times less powerful than your machine&#39;s.&lt;/li&gt;
&lt;li&gt;Make sure that the &lt;strong&gt;Clear Storage&lt;/strong&gt; checkbox is enabled. This option
forces Lighthouse to go to the network for every page resource, which is
how first-time visitors experience the page.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Run Audits&lt;/strong&gt;. After 5 to 10 seconds, Lighthouse shows you a report.&lt;/li&gt;
&lt;/ol&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-3.png&quot; alt=&quot;DevTools showing a Lighthouse audit results report.&quot;&gt;
&lt;div class=&quot;w-aside w-aside--note&quot;&gt;
&lt;p&gt;You can set the configuration options to whatever makes the most sense for
your needs. If you don&#39;t understand them, the ones mentioned here are good
defaults. If you can get your page fast with these options, then your page will
be fast for everyone. The important thing is to stay consistent with the options
across audits.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For example, if you run some audits with &lt;strong&gt;Simulated Fast 3G&lt;/strong&gt;, &lt;strong&gt;4x CPU Slowdown
throttling enabled&lt;/strong&gt; and then other times you run audits with throttling
disabled, your metrics scores will be significantly lower when you have
throttling enabled. You might spend a lot of time trying to figure out why your
page is so much slower now, when in reality the only thing that changed was your
configuration.&lt;/p&gt;
&lt;h3 id=&quot;understand-your-report&quot;&gt;Understand your report &lt;a class=&quot;w-headline-link&quot; href=&quot;#understand-your-report&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The top-right of your report lists your overall performance score. 100 is a
perfect score. Below the overall score are the metrics scores.
&lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/v3/scoring&quot;&gt;Lighthouse v3 Scoring Guide&lt;/a&gt;
explains how each metric score contributes to the overall score.&lt;/p&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-4.png&quot; alt=&quot;Lighthouse metrics scores showing green, passing scores, and yellow, warning scores.&quot;&gt;
&lt;p&gt;Hover over a metric to learn more about it. Click &lt;strong&gt;Learn more&lt;/strong&gt; to read
documentation about it.&lt;/p&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-5.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Below your metrics scores you see screenshots of how the page looked while it
loaded.&lt;/p&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-6.png&quot; alt=&quot;DevTools&#39; filmstrip view of a page loading.&quot;&gt;
&lt;p&gt;Below the screenshots you see opportunities for improving the page&#39;s
performance.&lt;/p&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-7.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Click an opportunity to learn more about it.&lt;/p&gt;
&lt;img class=&quot;w-screenshot w-screenshot--filled&quot; src=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/discover-performance-opportunities-with-lighthouse-8.png&quot; alt=&quot;An expanded audit titled Defer offscreen images shows a number of image paths that can be optimized.&quot;&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a class=&quot;w-headline-link&quot; href=&quot;#next-steps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Try using Lighthouse to audit your page, either from your profile page or from
Chrome DevTools. Implement one of the opportunities, and then audit your page
again to see how the change affected your report. Your metrics scores should
ideally be a little better, and Lighthouse should no longer be flagging that
opportunity as something to work on.&lt;/p&gt;
&lt;p&gt;Running Lighthouse yourself is great for spot-checking issues, but ultimately
you&#39;ll want to setup continuous monitoring to make sure your site stays healthy.
To track your Lighthouse progress over time add your site to the your
&lt;a href=&quot;https://web.dev/measure&quot;&gt;profile&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name> </name>
    </author>
  </entry>
</feed>