<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Ewa Gasperowicz on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Ewa Gasperowicz</name>
  </author>
  <link href="https://web.dev/authors/ewagasperowicz/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/K5zWxdTH5JagItd1w8Vm.jpeg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Ewa is a contributor to web.dev</subtitle>
  
  
  <entry>
    <title>Web Performance Made Easy - Google I/O 2018 edition</title>
    <link href="https://web.dev/web-performance-made-easy/"/>
    <updated>2018-08-21T00:00:00Z</updated>
    <id>https://web.dev/web-performance-made-easy/</id>
    <content type="html" mode="escaped">&lt;p&gt;We&#39;ve been pretty busy over the past year trying to figure out how to make the Web faster and
more performant. This led to new tools, approaches and libraries that we’d like share with you
in this article. In the first part, we’ll show you some optimization techniques we used in practice
when developing &lt;a href=&quot;https://github.com/google/oodle-demo&quot; rel=&quot;noopener&quot;&gt;The Oodles Theater app&lt;/a&gt;. In the second part,
we’ll talk about our experiments with predictive loading and the new
&lt;a href=&quot;https://github.com/guess-js/guess&quot; rel=&quot;noopener&quot;&gt;Guess.js&lt;/a&gt; initiative.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Prefer a video to an article? You can watch the presentation on which this was based instead: &lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;Mv-l3-tJgGk&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-need-for-performance&quot;&gt;The need for performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#the-need-for-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Internet gets heavier and heavier every year. If we check
&lt;a href=&quot;https://httparchive.org/reports/state-of-the-web&quot; rel=&quot;noopener&quot;&gt;the state of the web&lt;/a&gt; we can see that a median
page on mobile weights at about 1.5MB, with the majority of that being JavaScript and images.&lt;/p&gt;
&lt;p&gt;The growing size of the websites, together with other factors, like network latency,
CPU limitations, render blocking patterns or superfluous third-party code, contributes to
the complicated performance puzzle.&lt;/p&gt;
&lt;p&gt;Most users rate speed as being at the very top of the UX hierarchy of their needs. This isn&#39;t too
surprising, because you can&#39;t really do a whole lot until a page is finished loading. You can&#39;t
derive value from the page, you can&#39;t admire its aesthetics.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;UX hierarchy piramide&quot; decoding=&quot;async&quot; height=&quot;338&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Z1NCy55gqEtPfksngiqo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 1.&lt;/strong&gt; How important is speed to users? (Speed Matters, Vol. 3)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We know that performance matters to the users, but it can also feel like a secret discovering
where to start optimizing. Fortunately, there are tools that can help you on the way.&lt;/p&gt;
&lt;h2 id=&quot;lighthouse-a-base-for-performance-workflow&quot;&gt;Lighthouse - a base for performance workflow &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#lighthouse-a-base-for-performance-workflow&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; is a part of Chrome DevTools
that allows you to make an audit of your website, and gives you hints on how to make it better.&lt;/p&gt;
&lt;p&gt;We recently launched a bunch of
&lt;a href=&quot;https://developer.chrome.com/blog/lighthouse-load-performance/&quot; rel=&quot;noopener&quot;&gt;new performance audits&lt;/a&gt;
that are really useful in everyday development workflow.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;New Lighthouse audits&quot; decoding=&quot;async&quot; height=&quot;347&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fnXQDZFYbg9SDNagLlew.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 2.&lt;/strong&gt; New Lighthouse audits
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let’s explore how you can take advantage of them on a practical example:
&lt;a href=&quot;https://oodle-demo.firebaseapp.com/&quot; rel=&quot;noopener&quot;&gt;The Oodles Theater app&lt;/a&gt;. It’s a little demo web app,
where you can try out some of our favourite interactive Google Doodles and even play a game or two.&lt;/p&gt;
&lt;p&gt;While building the app, we wanted to make sure that it was as performant as possible. The starting
point for optimization was a Lighthouse report.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse report for Oodles app&quot; decoding=&quot;async&quot; height=&quot;490&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/euuEKKlTGjpPfLVdyN1i.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 3.&lt;/strong&gt; Lighthouse report for Oodles app
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The initial performance of our app as seen on Lighthouse report was pretty terrible.
On a 3G network, the user needed to wait for 15 seconds for the first meaningful paint, or for
the app to get interactive. Lighthouse highlighted a ton of issues with our site, and the overall
performance score of &lt;strong&gt;23&lt;/strong&gt; mirrored exactly that.&lt;/p&gt;
&lt;p&gt;The page weighted about &lt;strong&gt;3.4MB&lt;/strong&gt; - we desperately needed to cut some fat.&lt;/p&gt;
&lt;p&gt;This started our first performance challenge: find things that we can easily remove without
affecting the overall experience.&lt;/p&gt;
&lt;h2 id=&quot;performance-optimization-opportunities&quot;&gt;Performance optimization opportunities &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#performance-optimization-opportunities&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;remove-unnecessary-resources&quot;&gt;Remove unnecessary resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#remove-unnecessary-resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are some obvious things that can be safely removed:  whitespace and comments.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Gains from minification&quot; decoding=&quot;async&quot; height=&quot;325&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 586px) 586px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/fX7mxoJmL1y6ZYTuSp6A.png?auto=format&amp;w=1172 1172w&quot; width=&quot;586&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 4.&lt;/strong&gt; Minify and compress JavaScript and CSS
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Lighthouse highlights this opportunity in the &lt;strong&gt;Unminified CSS &amp;amp; JavaScript audit&lt;/strong&gt;. We were using
webpack for our build process, so in order to get minification we simply used the
&lt;a href=&quot;https://webpack.js.org/plugins/uglifyjs-webpack-plugin/&quot; rel=&quot;noopener&quot;&gt;Uglify JS plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Minification is a common task, so you should be able to find a ready-made solution for whichever
build process you happen to use.&lt;/p&gt;
&lt;p&gt;Another useful audit in that space is &lt;strong&gt;Enable text compression&lt;/strong&gt;. There is no reason to
send uncompressed files, and most of
the &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/CDN&quot; rel=&quot;noopener&quot;&gt;CDNs&lt;/a&gt; support this out of the
box these days.&lt;/p&gt;
&lt;p&gt;We were using &lt;a href=&quot;https://firebase.google.com/docs/hosting/&quot; rel=&quot;noopener&quot;&gt;Firebase Hosting&lt;/a&gt; to host our code,
and Firebase enables &lt;a href=&quot;https://www.gnu.org/software/gzip/&quot; rel=&quot;noopener&quot;&gt;gzipping&lt;/a&gt; by default, so by the sheer
virtue of hosting our code on a reasonable CDN we got that for free.&lt;/p&gt;
&lt;p&gt;While gzip is a very popular way of compressing, other mechanisms like
&lt;a href=&quot;https://github.com/google/zopfli&quot; rel=&quot;noopener&quot;&gt;Zopfli&lt;/a&gt; and &lt;a href=&quot;https://github.com/google/brotli&quot; rel=&quot;noopener&quot;&gt;Brotli&lt;/a&gt; are
getting traction as well. Brotli enjoys support in most browsers, and you can use a binary
to pre-compress your assets before sending them to the server.&lt;/p&gt;
&lt;h3 id=&quot;use-efficient-cache-policies&quot;&gt;Use efficient cache policies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#use-efficient-cache-policies&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our next step was to ensure that we don’t send resources twice if unnecessary.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Inefficient cache policy&lt;/strong&gt; audit in Lighthouse helped us notice that we could be optimizing
our caching strategies in order to achieve exactly that. By setting a max-age expiration header
in our server, we made sure that on a repeated visit the user can reuse the resources they have
downloaded before.&lt;/p&gt;
&lt;p&gt;Ideally you should aim at caching as many resources as securely possible for the longest possible
period of time and provide validation tokens for efficient revalidation of the resources
that got updated.&lt;/p&gt;
&lt;h3 id=&quot;remove-unused-code&quot;&gt;Remove unused code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#remove-unused-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So far we removed the obvious parts of the unnecessary download, but what about the less obvious
parts? For example, unused code.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Code coverage in DevTools&quot; decoding=&quot;async&quot; height=&quot;275&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1FjR76i7lLptY08Djx1R.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 5.&lt;/strong&gt; Check code coverage
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Sometimes we include in our apps code that is not really necessary. This happens especially
if you work on your app for a longer period of time, your team or your dependencies change,
and sometimes an orphan library gets left behind. That&#39;s exactly what happened to us.&lt;/p&gt;
&lt;p&gt;At the beginning we were using Material Components library to quickly prototype our app.
In time we moved to a more custom look and feel and we forgot entirely about that library.
Fortunately, the &lt;strong&gt;code coverage&lt;/strong&gt; check helped us rediscover it in our bundle.&lt;/p&gt;
&lt;p&gt;You can check your code coverage stats in DevTools, both for the runtime as well as load time of
your application. You can see the two big red stripes in the bottom screenshot - we had over 95%
of our CSS unused, and a big bunch of JavaScript as well.&lt;/p&gt;
&lt;p&gt;Lighthouse also picked up this issue in the unused CSS rules audit. It showed a potential saving
of over 400kb. So we got back to our code and we removed both the JavaScript and CSS part
of that library.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;If we drop MVC adapter our styles drop to 10KB&quot; decoding=&quot;async&quot; height=&quot;272&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WjgWvff6PiQwMrRL5ObT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 6.&lt;/strong&gt; If we drop MVC adapter our styles drop to 10KB!
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This brought our CSS bundle down 20-fold, which is pretty good for a tiny, two-line-long commit.&lt;/p&gt;
&lt;p&gt;Of course, it made our performance score go up, and also the
&lt;a href=&quot;https://developer.akamai.com/blog/2017/04/12/gauge-user-experience-time-interactive/&quot; rel=&quot;noopener&quot;&gt;Time to Interactive&lt;/a&gt;
got much better.&lt;/p&gt;
&lt;p&gt;However, with changes like this, it’s not enough to check your metrics and scores alone.
Removing actual code is never risk-free, so you should always look out for potential regressions.&lt;/p&gt;
&lt;p&gt;Our code was unused in 95% - there’s still this 5% somewhere. Apparently one of our components
was still using the styles from that library - the little arrows in the doodle slider. Because it
was so small though, we could just go and manually incorporate those styles back into the buttons.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Buttons got broken by missing library&quot; decoding=&quot;async&quot; height=&quot;196&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 588px) 588px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/Ah5S9a2Ki5oq9lHlzrGK.png?auto=format&amp;w=1176 1176w&quot; width=&quot;588&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 7.&lt;/strong&gt; One component was still using the removed library
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So if you remove code, just make sure you have a proper testing workflow in place to help you
guard against potential visual regressions.&lt;/p&gt;
&lt;h3 id=&quot;avoid-enormous-network-payloads&quot;&gt;Avoid enormous network payloads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#avoid-enormous-network-payloads&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We know that large resources can slow down web page loads. They can cost our users money and
they can have a big impact on their data plans, so it&#39;s really important to be mindful of this.&lt;/p&gt;
&lt;p&gt;Lighthouse was able to detect that we had an issue with some of our network payloads using the
&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight&quot; rel=&quot;noopener&quot;&gt;Enormous network payload&lt;/a&gt;
audit.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Detect enormous network payloads&quot; decoding=&quot;async&quot; height=&quot;473&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/QvmoocK29nYcmZBiVP6o.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 8.&lt;/strong&gt; Detect enormous network payloads
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Here we saw that we had over 3mb worth of code that was being shipped down – which is quite a lot,
especially on mobile.&lt;/p&gt;
&lt;p&gt;At the very top of this list, Lighthouse highlighted that we had a JavaScript vendor bundle
that was 2mb of uncompressed code. This is also a problem highlighted by webpack.&lt;/p&gt;
&lt;p&gt;As the saying goes: the fastest request is the one that&#39;s not made.&lt;/p&gt;
&lt;p&gt;Ideally you should be measuring the value of every single asset you&#39;re serving down to your users,
measuring the performance of those assets, and making a call on whether it&#39;s worth actually shipping
down with the initial experience. Because sometimes these assets can be deferred, or lazily loaded,
or processed during idle time.&lt;/p&gt;
&lt;p&gt;In our case, because we&#39;re dealing with a lot of JavaScript bundles, we were fortunate because
the JavaScript community has a rich set of JavaScript bundle auditing tools.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;JavaScript bundle auditing&quot; decoding=&quot;async&quot; height=&quot;398&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GLlPM0f586jIEl1xRVoQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 9.&lt;/strong&gt; JavaScript bundle auditing
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We started off with webpack bundle analyzer, which informed us that we were including a
dependency called unicode which was 1.6mb of parsed JavaScript, so quite a lot.&lt;/p&gt;
&lt;p&gt;We then went over to our editor and using the
&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost&quot; rel=&quot;noopener&quot;&gt;Import Cost Plugin for Visual code&lt;/a&gt;
we were able to
visualize the cost of every module that we were importing. This allowed us to discover which
component was including code that was referencing this module.&lt;/p&gt;
&lt;p&gt;We then switched over to another tool, &lt;a href=&quot;https://bundlephobia.com/&quot; rel=&quot;noopener&quot;&gt;BundlePhobia&lt;/a&gt;. This is a tool
which allows you to enter in the name of any NPM package and actually see what its minified and
gzipped size is estimated to be. We found a nice alternative for the slug module we were using
that only weighed 2.2kb, and so we switched that up.&lt;/p&gt;
&lt;p&gt;This had a big impact on our performance. Between this change and discovering other opportunities
to trim down our JavaScript bundle size, we saved 2.1mb of code.&lt;/p&gt;
&lt;p&gt;We saw 65% improvements overall, once you factor in the gzipped and minified size of these bundles.
And we found that this was really worth doing as a process.&lt;/p&gt;
&lt;p&gt;So, in general, try to eliminate unnecessary downloads in your sites and apps.
Make an inventory of your assets and measure their performance impact can make a really big
difference, so make sure that you&#39;re auditing your assets fairly regularly.&lt;/p&gt;
&lt;h3 id=&quot;lower-javascript-boot-up-time-with-code-splitting&quot;&gt;Lower JavaScript boot-up time with code splitting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#lower-javascript-boot-up-time-with-code-splitting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although large network payloads can have a big impact on our app, there&#39;s another thing that can
have a really big impact, and that is JavaScript.&lt;/p&gt;
&lt;p&gt;JavaScript is your
&lt;a href=&quot;https://medium.com/dev-channel/the-cost-of-javascript-84009f51e99e&quot; rel=&quot;noopener&quot;&gt;most expensive asset&lt;/a&gt;.
On mobile, if you&#39;re sending
down large bundles of JavaScript, it can delay how soon your users are able to interact with your
user interface components. That means they can be tapping on UI without anything meaningful
actually happening. So it&#39;s important for us to understand why JavaScript costs so much.&lt;/p&gt;
&lt;p&gt;This is how a browser processes JavaScript.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;JavaScript processing&quot; decoding=&quot;async&quot; height=&quot;328&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 589px) 589px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/8cEoOq6exz9QLDLfNB6q.png?auto=format&amp;w=1178 1178w&quot; width=&quot;589&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 10.&lt;/strong&gt; JavaScript processing
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We first of all have to download that script, we have a JavaScript engine which then needs to parse
that code, needs to compile it and execute it.&lt;/p&gt;
&lt;p&gt;Now these phases are something that don&#39;t take a whole lot of time on a high-end device like
a desktop machine or a laptop, maybe even a high-end phone. But on a median mobile phone this
process can take anywhere between five and ten times longer. This is what delays interactivity,
so it&#39;s important for us to try trimming this down.&lt;/p&gt;
&lt;p&gt;To help you discover these issues with your app, we introduced a new
&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/bootup-time&quot; rel=&quot;noopener&quot;&gt;JavaScript boot-up time audit&lt;/a&gt; to Lighthouse.&lt;/p&gt;
&lt;figure&gt;
 &lt;img alt=&quot;JavaScript boot up time&quot; decoding=&quot;async&quot; height=&quot;223&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDj3tb1r3VrmPbrDo1kr.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 11.&lt;/strong&gt; JavaScript boot up time audit
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And in the case of the Oodle app, it told us that we had 1.8 seconds of time spent in JavaScript
boot-up. What was happening was that we were statically importing in all of our routes and
components into one monolithic JavaScript bundle.&lt;/p&gt;
&lt;p&gt;One technique for working around this is using code splitting.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot;&gt;
  &lt;img alt=&quot;Code splitting is like pizza&quot; decoding=&quot;async&quot; height=&quot;257&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/RH8lhpMF9WUaqtLD69VB.png?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Code splitting is this notion of instead of giving your users a whole pizza’s worth of JavaScript,
what if you only gave them one slice at a time as they needed it?&lt;/p&gt;
&lt;p&gt;Code splitting can be applied at a route level or a component level. It works great with React and
React Loadable, Vue.js, Angular, Polymer, Preact, and multiple other libraries.&lt;/p&gt;
&lt;p&gt;We incorporated code splitting into our application, we switched over from static imports to
dynamic imports, allowing us to asynchronously lazy load code in as we needed it.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Code splitting with dynamic imports&quot; decoding=&quot;async&quot; height=&quot;382&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a0wDugnVMM1WjNyAvLEv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 13.&lt;/strong&gt; Code splitting with dynamic imports
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The impact this had was both shrinking down the size of our bundles, but also decreasing our
JavaScript boot up time. It took it down to 0.78 seconds, making the app 56% faster.&lt;/p&gt;
&lt;p&gt;In general, if you&#39;re building a JavaScript-heavy experience, be sure to only send code to
the user that they need.&lt;/p&gt;
&lt;p&gt;Take advantage of concepts like code splitting, explore ideas like tree shaking, and check out
&lt;a href=&quot;https://github.com/GoogleChromeLabs/webpack-libs-optimizations&quot; rel=&quot;noopener&quot;&gt;webpack-libs-optimizations&lt;/a&gt;
repo for a few ideas on how you can trim down your library size if you happen to be using webpack.&lt;/p&gt;
&lt;h3 id=&quot;optimize-images&quot;&gt;Optimize images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#optimize-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Image loading performance joke&quot; decoding=&quot;async&quot; height=&quot;323&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 586px) 586px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/UeTyldEMnrV4Bn67mdJO.png?auto=format&amp;w=1172 1172w&quot; width=&quot;586&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In the Oodle app we&#39;re using a lot of images. Unfortunately, Lighthouse was much less enthusiastic
about it than we were. As a matter of fact, we failed on all three image-related audits.&lt;/p&gt;
&lt;p&gt;We forgot to optimize our images, we were not sizing them correctly, and also we could get some
gain from using other image formats.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Image audits&quot; decoding=&quot;async&quot; height=&quot;325&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 588px) 588px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lH2NxcPFVetLrvV7KZOG.png?auto=format&amp;w=1176 1176w&quot; width=&quot;588&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 14.&lt;/strong&gt; Lighthouse image audits
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We started with optimizing our images.&lt;/p&gt;
&lt;p&gt;For one-off optimization round you can use visual tools like
&lt;a href=&quot;https://imageoptim.com/mac&quot; rel=&quot;noopener&quot;&gt;ImageOptim&lt;/a&gt; or &lt;a href=&quot;https://www.xnview.com/en/xnconvert/&quot; rel=&quot;noopener&quot;&gt;XNConvert&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A more automated approach is to add an image optimization step to your build process, with
libraries like &lt;a href=&quot;https://github.com/imagemin/imagemin&quot; rel=&quot;noopener&quot;&gt;imagemin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This way you make sure that the images added in the future get optimized automatically.
Some CDNs, for example &lt;a href=&quot;https://www.akamai.com/&quot; rel=&quot;noopener&quot;&gt;Akamai&lt;/a&gt; or third-party solutions like
&lt;a href=&quot;https://cloudinary.com/&quot; rel=&quot;noopener&quot;&gt;Cloudinary&lt;/a&gt;, &lt;a href=&quot;https://www.fastly.com/&quot; rel=&quot;noopener&quot;&gt;Fastly&lt;/a&gt;
or &lt;a href=&quot;https://uploadcare.com/&quot; rel=&quot;noopener&quot;&gt;Uploadcare&lt;/a&gt; offer you comprehensive
image optimization solutions. so you can also simply host your images on those services.&lt;/p&gt;
&lt;p&gt;If you don&#39;t want to do that because of the cost, or latency issues, projects like
&lt;a href=&quot;http://thumbor.org/&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; or &lt;a href=&quot;https://github.com/imazen/imageflow&quot; rel=&quot;noopener&quot;&gt;Imageflow&lt;/a&gt; offer
self-hosted alternatives.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Before and after optimization&quot; decoding=&quot;async&quot; height=&quot;164&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 513px) 513px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/1Jyf3Tn6cA4Z2slp26QF.png?auto=format&amp;w=1026 1026w&quot; width=&quot;513&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 15.&lt;/strong&gt; Before and after optimization
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Our background PNG was flagged in webpack as big, and rightly so. After sizing it correctly to
the viewport and running it through ImageOptim, we went down to 100kb, which is acceptable.&lt;/p&gt;
&lt;p&gt;Repeating this for multiple images on our site allowed us to bring down the overall page weight
significantly.&lt;/p&gt;
&lt;h3 id=&quot;use-the-right-format-for-animated-content&quot;&gt;Use the right format for animated content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#use-the-right-format-for-animated-content&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GIFs can get really expensive. Surprisingly, the GIF format was never intended as an animation
platform in the first place. Therefore, switching to a more suitable video format offers you large
savings in terms of file size.&lt;/p&gt;
&lt;p&gt;In Oodle app, we were using a GIF as an intro sequence on the home page. According to Lighthouse,
we could be saving over 7mb by switching to a more efficient video format. Our clip weighted about
7.3mb, way too much for any reasonable website, so instead we turned it into a video element with
two source files - an mp4 and WebM for wider browser support.&lt;/p&gt;
&lt;figure style=&quot;text-align: center;&quot;&gt;
    &lt;img alt=&quot;Replace Animated GIFs with video&quot; decoding=&quot;async&quot; height=&quot;302&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/PXR11JRtDWpkMEXrx48Z.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 16.&lt;/strong&gt; Replace Animated GIFs with video
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We used the &lt;a href=&quot;https://ffmpeg.org/&quot; rel=&quot;noopener&quot;&gt;FFmpeg&lt;/a&gt; tool to convert our animation GIF into the mp4 file.
The WebM format offers you even larger savings - the ImageOptim API can do such conversion for you.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;ffmpeg -i animation.gif -b:v &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; -crf &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt; -vf &lt;span class=&quot;token assign-left variable&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;600&lt;/span&gt;:-1 video.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;We managed to save over 80% of our overall weight thanks to this conversion. This brought
us down to around 1mb.&lt;/p&gt;
&lt;p&gt;Still, 1mb is a large resource to push down the wire, especially for a user on a restricted
bandwidth. Luckily we could use
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation/effectiveType&quot; rel=&quot;noopener&quot;&gt;Effective Type API&lt;/a&gt;
to realize they&#39;re on a slow bandwidth, and give them a much smaller JPEG instead.&lt;/p&gt;
&lt;p&gt;This interface uses the effective round-trip time and downing values to estimate the network type
the user is using. It simply returns a string, slow 2G, 2G, 3G or 4G. So depending on
this value, if the user is on below 4G we could replace the video element with the image.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;effectiveType&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; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It does remove a little bit from the experience, but at least the site is usable on a slow
connection.&lt;/p&gt;
&lt;h3 id=&quot;lazy-load-off-screen-images&quot;&gt;Lazy-load off-screen images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#lazy-load-off-screen-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Carousels, sliders, or really long pages often load images, even though the user cannot see them
on the page straight away.&lt;/p&gt;
&lt;p&gt;Lighthouse will flag this behavior in the off-screen images audit, and you can also see it
for yourself in the network panel of DevTools. If you see a lot of images incoming while only
a few are visible on the page, it means that maybe you could consider lazy loading them instead.&lt;/p&gt;
&lt;p&gt;Lazy loading is not yet supported natively in the browser, so we have to use JavaScript to add this
capability. We used &lt;a href=&quot;https://www.npmjs.com/package/lazysizes&quot; rel=&quot;noopener&quot;&gt;Lazysizes library&lt;/a&gt; to add lazy
loading behavior to our Oodle covers.&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;token comment&quot;&gt;&amp;lt;!-- Import library --&gt;&lt;/span&gt;&lt;br /&gt;import lazysizes from &#39;lazysizes&#39;  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- or --&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 attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazysizes.min.js&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 script&quot;&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Use it --&gt;&lt;/span&gt;&lt;br /&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;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 attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image.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;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&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;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;img&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 attr-equals&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;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;data-sizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;auto&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;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image2.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;data-srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image1.jpg 300w,&lt;br /&gt;    image2.jpg 600w,&lt;br /&gt;    image3.jpg 900w&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;/div&gt;&lt;p&gt;Lazysizes is smart because it does not only track the visibility changes of the element, but it
also proactively prefetches elements that are near the view for the optimal user experience.
It also offers an optional integration of the &lt;code&gt;IntersectionObserver&lt;/code&gt;, which gives you very
efficient visibility lookups.&lt;/p&gt;
&lt;p&gt;After this change our images are being fetched on-demand. If you want to dig deeper into that
topic, check out &lt;a href=&quot;https://images.guide/&quot; rel=&quot;noopener&quot;&gt;images.guide&lt;/a&gt; - a very handy and comprehensive resource.&lt;/p&gt;
&lt;h3 id=&quot;help-browser-deliver-critical-resources-early&quot;&gt;Help browser deliver critical resources early &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#help-browser-deliver-critical-resources-early&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Not every byte that&#39;s shipped down the wire to the browser has the same degree of importance,
and the browser knows this. A lot of browsers have heuristics to decide what they should be
fetching first. So sometimes they&#39;ll fetch CSS before images or scripts.&lt;/p&gt;
&lt;p&gt;Something that could be useful is us, as authors of the page, informing the browser what&#39;s
actually really important to us. Thankfully, over the last couple of years browser vendors have
been adding a number of features to help us with this, e.g.
&lt;a href=&quot;https://web.dev/web-performance-made-easy/(https://www.keycdn.com/blog/resource-hints/)&quot;&gt;resource hints&lt;/a&gt; like &lt;code&gt;link rel=preconnect&lt;/code&gt;,
or &lt;code&gt;preload&lt;/code&gt; or &lt;code&gt;prefetch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These capabilities that were brought to the web platform help the browser fetch the right thing
at the right time, and they can be a little bit more efficient than some of the custom loading,
logic-based approaches that are done using script instead.&lt;/p&gt;
&lt;p&gt;Let&#39;s see how Lighthouse actually guides us towards using some of these features effectively.&lt;/p&gt;
&lt;p&gt;The first thing Lighthouse tells us to do is avoid multiple costly round trips to any origin.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Avoid multiple, costly round trips to any origin&quot; decoding=&quot;async&quot; height=&quot;360&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/GWsRfe3VEFOuHwgycWMS.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 17.&lt;/strong&gt; Avoid multiple, costly round trips to any origin
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the case of the Oodle app, we’re actually heavily using Google Fonts. Whenever you drop a
Google Font stylesheet into your page it&#39;s going to connect up to two subdomains. And what
Lighthouse is telling us is that if we were able to warm up that connection, we could save
anywhere up to 300 milliseconds in our initial connection time.&lt;/p&gt;
&lt;p&gt;Taking advantage of link rel preconnect, we can effectively mask that connection latency.&lt;/p&gt;
&lt;p&gt;Especially with something like Google Fonts where our font face CSS is hosted
on &lt;a href=&quot;https://en.wikipedia.org/wiki/Google_APIs&quot; rel=&quot;noopener&quot;&gt;googleapis.com&lt;/a&gt;, and our font
resources are hosted on &lt;a href=&quot;https://superuser.com/a/64724&quot; rel=&quot;noopener&quot;&gt;Gstatic&lt;/a&gt;, this can have a really big
impact. So we applied this optimization and we shaved off a few hundred milliseconds.&lt;/p&gt;
&lt;p&gt;The next thing that Lighthouse suggests is that we preload key requests.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Preload key requests&quot; decoding=&quot;async&quot; height=&quot;366&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lt3Nvi9UtaejjVYHbGp9.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 18.&lt;/strong&gt; Preload key requests
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt; is really powerful, it informs the browser that a resource is needed as
part of the current navigation, and it tries to get the browser fetching it as soon as possible.&lt;/p&gt;
&lt;p&gt;Now here Lighthouse is telling us that we should be going and preloading our key web font
resources, because we&#39;re loading in two web fonts.&lt;/p&gt;
&lt;p&gt;Preloading in a web font looks like this - specifying &lt;code&gt;rel=preload&lt;/code&gt;, you pass in &lt;code&gt;as&lt;/code&gt; with
the type of font, and then you specify the type of font you&#39;re trying to load in, such as woff2.&lt;/p&gt;
&lt;p&gt;The impact this can have on your page is quite stark.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Impact of preloading resources&quot; decoding=&quot;async&quot; height=&quot;326&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 585px) 585px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jDWc5PChSWiCbVZKlMDt.png?auto=format&amp;w=1170 1170w&quot; width=&quot;585&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 19.&lt;/strong&gt; Impact of preloading resources
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Normally, without using link rel preload, if web fonts happen to be critical to your page, what
the browser has to do is it first of all has to fetch your HTML, has to parse your CSS, and
somewhere much later down the line, it&#39;ll finally go and fetch your web fonts.&lt;/p&gt;
&lt;p&gt;Using link rel preload, as soon as the browser has parsed your HTML it can actually start fetching
those web fonts much earlier on. In the case of our app, this was able to shave a second off the
time it took for us to render text using our web fonts.&lt;/p&gt;
&lt;p&gt;Now it&#39;s not quite that straightforward if you&#39;re going to try preloading fonts using Google Fonts,
there is one gotcha.&lt;/p&gt;
&lt;p&gt;The Google Font URLs that we specify on our font faces in our stylesheets happened to be something
that the fonts team update fairly regularly. These URLs can expire, or get updated on a regular
frequency, and so what we would suggest to do if you want complete control over your font loading
experience is to self-host your web fonts. This can be great because it gives you access
to things like link rel preload.&lt;/p&gt;
&lt;p&gt;In our case we found the tool
&lt;a href=&quot;https://google-webfonts-helper.herokuapp.com/fonts&quot; rel=&quot;noopener&quot;&gt;Google Web Fonts Helper&lt;/a&gt;
really useful in helping us offline some of those web fonts and set them up locally, so check that
tool out.&lt;/p&gt;
&lt;p&gt;Whether you&#39;re using web fonts as part of your critical resources, or it happens to be JavaScript,
try to help the browser deliver your critical resources as soon as possible.&lt;/p&gt;
&lt;h3 id=&quot;experimental-priority-hints&quot;&gt;Experimental: Priority Hints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#experimental-priority-hints&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We&#39;ve got something special to share with you today. In addition to features like resource hints,
as well as preload, we&#39;ve been working on a brand new experimental browser feature we’re calling
priority hints.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Set priority for the initially visible content&quot; decoding=&quot;async&quot; height=&quot;326&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 585px) 585px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/jx7YvykNpmjO39vsrST5.png?auto=format&amp;w=1170 1170w&quot; width=&quot;585&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 20.&lt;/strong&gt; Priority hints
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is a new feature that allows you to hint to the browser how important a resource is. It
exposes a new attribute - importance - with the values low, high or auto.&lt;/p&gt;
&lt;p&gt;This allows us to convey lowering the priority of less important resources, such as non-critical
styles, images, or fetch API calls to reduce contention. We can also boost the priority of more
important things, like our hero images.&lt;/p&gt;
&lt;p&gt;In the case of our Oodle app, this actually led to one practical place where we could optimize.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Set priority for the initially visible content&quot; decoding=&quot;async&quot; height=&quot;334&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 587px) 587px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/IZtNqC7SDTVYQol6OOTF.png?auto=format&amp;w=1174 1174w&quot; width=&quot;587&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 21.&lt;/strong&gt; Set priority for the initially visible content
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Before we added lazy loading to our images, what the browser was doing is, we had this image
carousel with all of our doodles and the browser was fetching all the images at the very start
of the carousel with a high priority early on. Unfortunately, it was the images in the middle
of the carousel that were most important to the user. So what we did was, we set the importance
of those background images to very low, the foreground ones to very high, and what this had was
a two second impact over slow 3G, and how quickly we were able to fetch and render those images.
So a nice positive experience.&lt;/p&gt;
&lt;p&gt;We&#39;re hoping to bring this feature to &lt;a href=&quot;https://www.google.com/chrome/browser/canary.html&quot; rel=&quot;noopener&quot;&gt;Canary&lt;/a&gt;
in a few weeks, so keep an eye out for that.&lt;/p&gt;
&lt;h3 id=&quot;have-a-web-font-loading-strategy&quot;&gt;Have a web font loading strategy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#have-a-web-font-loading-strategy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Typography is fundamental to good design, and if you&#39;re using web fonts you ideally don&#39;t want to
block rendering of your text, and you definitely don&#39;t want to show invisible text.&lt;/p&gt;
&lt;p&gt;We highlight this in Lighthouse now, with the &lt;strong&gt;avoid invisible text while web fonts are loading&lt;/strong&gt;
audit.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Avoid invisible text while Web Fonts are loading&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/CamYoLwwQHGXmDyBXIr1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 22.&lt;/strong&gt; Avoid invisible text while Web Fonts are loading
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you load your web fonts using a font face block, you&#39;re letting the browser decide what to do
if it takes a long time for that web font to fetch. Some browsers will wait anywhere up
to three seconds for this before falling back to a system font, and they&#39;ll eventually swap it
out to the font once it&#39;s downloaded.&lt;/p&gt;
&lt;p&gt;We&#39;re trying to avoid this invisible text, so in this case we wouldn&#39;t have been able to see this
week&#39;s classic doodles if the web font had taken too long. Thankfully, with a new feature called
&lt;code&gt;font-display&lt;/code&gt;, you actually get a lot more control over this process.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;    &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Montserrat&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Montserrat Regular&#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;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Montserrat-Regular&#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;br /&gt;          &lt;span class=&quot;token comment&quot;&gt;/* Chrome 26+, Opera 23+, Firefox 39+ */&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;montserrat-v12-latin-regular.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#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;br /&gt;            &lt;span class=&quot;token comment&quot;&gt;/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;montserrat-v12-latin-regular.woff&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Font display helps you decide how web fonts will render or fallback based on how long it takes
for them to swap.&lt;/p&gt;
&lt;p&gt;In this case we&#39;re using font display swap. Swap gives the font face a zero second block period,
and an infinite swap period. This means the browser&#39;s going to draw your text pretty immediately
with a fallback font if the font takes a while to load. And it&#39;s going to swap it once the font
face is available.&lt;/p&gt;
&lt;p&gt;In the case of our app, why this was great is that it allowed us to display some meaningful text
very early on, and transition over to the web font once it was ready.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Font display outcome&quot; decoding=&quot;async&quot; height=&quot;331&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 585px) 585px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/54dx67WBnfcKzjduM4HT.png?auto=format&amp;w=1170 1170w&quot; width=&quot;585&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 23.&lt;/strong&gt; Font display outcome
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In general, if you happen to be using web fonts, as a large percentage of the web does, have
a good web font loading strategy in place.&lt;/p&gt;
&lt;p&gt;There are a lot of web platform features you can use to optimize your loading experience for fonts,
but also check out Zach Leatherman&#39;s &lt;a href=&quot;https://www.zachleat.com/web/recipes/&quot; rel=&quot;noopener&quot;&gt;Web Font Recipes repo&lt;/a&gt;,
because it&#39;s really great.&lt;/p&gt;
&lt;h3 id=&quot;reduce-render-blocking-scripts&quot;&gt;Reduce render-blocking scripts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#reduce-render-blocking-scripts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are other parts of our application that we could push earlier in the download chain to provide
at least some basic user experience a little bit earlier.&lt;/p&gt;
&lt;p&gt;On the Lighthouse timeline strip you can see that during these first few seconds when all the
resources are loading, the user cannot really see any content.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Reduce render-blocking stylesheets opportunity&quot; decoding=&quot;async&quot; height=&quot;259&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/HX1xfdh4LrEeUrMJWWYc.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 24.&lt;/strong&gt; Reduce render-blocking stylesheets opportunity
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Downloading and processing external stylesheets is blocking our rendering process from making
any progress.&lt;/p&gt;
&lt;p&gt;We can try to optimize our critical rendering path by delivering some of the styles a bit earlier.&lt;/p&gt;
&lt;p&gt;If we extract the styles that are responsible for this initial render and inline them in our HTML,
the browser is able to render them straight away without waiting for the external stylesheets
to arrive.&lt;/p&gt;
&lt;p&gt;In our case, we used an NPM module called &lt;a href=&quot;https://www.npmjs.com/package/critical&quot; rel=&quot;noopener&quot;&gt;Critical&lt;/a&gt;
to inline our critical content in index.html during a build step.&lt;/p&gt;
&lt;p&gt;While this module did most of the heavy lifting for us, it was still a little bit tricky to get
this working smoothly across different routes.&lt;/p&gt;
&lt;p&gt;If you are not careful or your site structure is really complex, it might be really difficult to
introduce this type of pattern if you did not plan for
&lt;a href=&quot;https://web.dev/learn/pwa/architecture/&quot;&gt;app shell architecture&lt;/a&gt;
from the beginning.&lt;/p&gt;
&lt;p&gt;This is why it&#39;s so important to take performance considerations early on. If you don&#39;t design for
performance from the start, there is a high chance that you will you run into issues doing it later.&lt;/p&gt;
&lt;p&gt;In the end our risk paid off, we managed to make it work and the app started delivering content much
earlier, improving our first meaningful paint time significantly.&lt;/p&gt;
&lt;h2 id=&quot;the-outcome&quot;&gt;The outcome &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#the-outcome&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That was a long list of performance optimizations we applied to our site. Let&#39;s take a look at the
outcome. This is how our app loaded on a medium mobile device on a 3G network, before and after
the optimization.&lt;/p&gt;
&lt;figure&gt;
  &lt;video&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/T4FyVKpzu4WKF1kBNvXepbi08t52/rGDjStPqX0zUn0X2D52W.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;The Lighthouse performance score went up from 23 to 91. That&#39;s pretty nice progress in terms
of speed. All of the changes were fueled by us continuously checking and following the Lighthouse
report. If you&#39;d like to check out how we technically implemented all of the improvements, feel
free to take a look at &lt;a href=&quot;http://github.com/google/oodle-demo&quot; rel=&quot;noopener&quot;&gt;our repo&lt;/a&gt;, especially at the PRs that
landed there.&lt;/p&gt;
&lt;h2 id=&quot;predictive-performance-data-driven-user-experiences&quot;&gt;Predictive performance  - data-driven user experiences &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#predictive-performance-data-driven-user-experiences&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We believe that machine learning represents an exciting opportunity for the future in many areas.
One idea that we hope will spark more experimentation in the future, is that real data can really
guide the user experiences we’re creating.&lt;/p&gt;
&lt;p&gt;Today, we make a lot of arbitrary decisions about what the user might want or need, and therefore
what is worth being prefetched, or preloaded, or pre-cached. If we guess right we are able to
prioritize a small amount of resources, but it&#39;s really hard to scale it to the whole website.&lt;/p&gt;
&lt;p&gt;We actually have data available to better inform our optimizations today.
Using the &lt;a href=&quot;https://developers.google.com/analytics/devguides/reporting/core/v4/&quot; rel=&quot;noopener&quot;&gt;Google Analytics reporting API&lt;/a&gt;
we can take a look at the next top page and exit
percentages for any URL on our site and therefore drive conclusions on which resources we should
prioritize.&lt;/p&gt;
&lt;p&gt;If we combine this with a good probability model, we avoid wasting our user’s data by aggressively
over-prefetching content. We can take advantage of that Google
Analytics data, and use machine learning and models like
&lt;a href=&quot;https://en.wikipedia.org/wiki/Markov_chain&quot; rel=&quot;noopener&quot;&gt;Markov chains&lt;/a&gt; or
&lt;a href=&quot;https://en.wikipedia.org/wiki/Artificial_neural_network&quot; rel=&quot;noopener&quot;&gt;neural network&lt;/a&gt; in order
to implement such models.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Data-driven Bundling for Web Apps&quot; decoding=&quot;async&quot; height=&quot;348&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eNZRHsxXBbFLRFSz3HOq.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 25.&lt;/strong&gt; Data-driven Bundling for Web Apps
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In order to facilitate this experiments, we&#39;re happy to announce a new initiative we&#39;re calling
&lt;a href=&quot;https://github.com/guess-js/guess&quot; rel=&quot;noopener&quot;&gt;Guess.js&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Guess.js&quot; decoding=&quot;async&quot; height=&quot;324&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 584px) 584px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zRGcCvp2LeX0aPebJK7K.png?auto=format&amp;w=1168 1168w&quot; width=&quot;584&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 26.&lt;/strong&gt; Guess.js
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Guess.js is a project focused on data-driven user experiences for the web. We hope that it&#39;s going
to inspire exploration of using data to improve web performance and go beyond that. It&#39;s all
open source and available on GitHub today. This was built in collaboration with the open source
community by Minko Gechev, Kyle Matthews from Gatsby, Katie Hempenius, and a number of others.&lt;/p&gt;
&lt;p&gt;Check out Guess.js, let us know what you think.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/web-performance-made-easy/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Scores and metrics are helpful in improving speed of the Web, but they are just the means,
not the goals themselves.&lt;/p&gt;
&lt;p&gt;We&#39;ve all experienced slow page loads on the go, but we now have an opportunity to give our
users more delightful experiences that load really quickly.&lt;/p&gt;
&lt;p&gt;Improving performance is a journey. Lots of small changes can lead to big gains. By using the
right optimization tools and keeping an eye on the Lighthouse reports, you can provide better
and more inclusive experience to your users.&lt;/p&gt;
&lt;p&gt;With special thanks to: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino,
Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse &amp;amp; Google Doodles.&lt;/p&gt;
</content>
    <author>
      <name>Ewa Gasperowicz</name>
    </author><author>
      <name>Addy Osmani</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – howto-checkbox</title>
    <link href="https://web.dev/components-howto-checkbox/"/>
    <updated>2017-10-02T00:00:00Z</updated>
    <id>https://web.dev/components-howto-checkbox/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-checkbox&amp;gt;&lt;/code&gt; represents a boolean option in a form. The most common type
of checkbox is a dual-type which allows the user to toggle between two
choices -- checked and unchecked.&lt;/p&gt;
&lt;p&gt;The element attempts to self apply the attributes &lt;code&gt;role=&amp;quot;checkbox&amp;quot;&lt;/code&gt; and
&lt;code&gt;tabindex=&amp;quot;0&amp;quot;&lt;/code&gt; when it is first created. The &lt;code&gt;role&lt;/code&gt; attribute helps assistive
technology like a screen reader tell the user what kind of control this is.
The &lt;code&gt;tabindex&lt;/code&gt; attribute opts the element into the tab order, making it keyboard
focusable and operable. To learn more about these two topics, check out
&lt;a href=&quot;https://web.dev/semantics-aria/#what-can-aria-do&quot;&gt;What can ARIA do?&lt;/a&gt; and &lt;a href=&quot;https://web.dev/using-tabindex/&quot;&gt;Using tabindex&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When the checkbox is checked, it adds a &lt;code&gt;checked&lt;/code&gt; boolean attribute, and sets
a corresponding &lt;code&gt;checked&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt;. In addition, the element sets an
&lt;code&gt;aria-checked&lt;/code&gt; attribute to either &lt;code&gt;&amp;quot;true&amp;quot;&lt;/code&gt; or &lt;code&gt;&amp;quot;false&amp;quot;&lt;/code&gt;, depending on its
state. Clicking on the checkbox with a mouse, or space bar, toggles these
checked states.&lt;/p&gt;
&lt;p&gt;The checkbox also supports a &lt;code&gt;disabled&lt;/code&gt; state. If either the &lt;code&gt;disabled&lt;/code&gt; property
is set to true or the &lt;code&gt;disabled&lt;/code&gt; attribute is applied, the checkbox sets
&lt;code&gt;aria-disabled=&amp;quot;true&amp;quot;&lt;/code&gt;, removes the &lt;code&gt;tabindex&lt;/code&gt; attribute, and returns focus
to the document if the checkbox is the current &lt;code&gt;activeElement&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The checkbox is paired with a &lt;code&gt;howto-label&lt;/code&gt; element to ensure it has an
&lt;a href=&quot;https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree#semantics_in_native_html&quot; rel=&quot;noopener&quot;&gt;accessible name&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Just because you &lt;em&gt;can&lt;/em&gt; build a custom element checkbox, doesn&#39;t necessarily mean that you &lt;em&gt;should&lt;/em&gt;. As this example shows, you will need to add your own keyboard, labeling, and ARIA support. It&#39;s also important to note that the native &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element will NOT submit values from a custom element. You will need to wire that up yourself using AJAX or a hidden &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; field. For these reasons it can often be preferable to use the built-in &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot;&amp;gt;&lt;/code&gt; instead. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/howto-components&quot; rel=&quot;noopener&quot;&gt;HowTo: Components on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/#checkbox&quot; rel=&quot;noopener&quot;&gt;Checkbox Pattern in ARIA Authoring Practices 1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/semantics-aria/#what-can-aria-do&quot;&gt;What can ARIA Do?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/using-tabindex/&quot;&gt;Using tabindex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://googlechromelabs.github.io/howto-components/howto-checkbox/#demo&quot; rel=&quot;noopener&quot;&gt;View live demo on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;example-usage&quot;&gt;Example usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#example-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;howto-checkbox&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;howto-label&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 8px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&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;howto-checkbox&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;join-checkbox&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;howto-checkbox&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;join-checkbox&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;Join Newsletter&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;howto-label&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&gt;&lt;h2 id=&quot;code&quot;&gt;Code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-checkbox/#code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Define key codes to help with handling keyboard events.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&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;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;SPACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Cloning contents from a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element is more performant than using innerHTML because it avoids additional HTML parse costs.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &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;template&#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;br /&gt;&lt;br /&gt;  template&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;    &amp;lt;style&gt;&lt;br /&gt;      :host {&lt;br /&gt;        display: inline-block;&lt;br /&gt;        background: url(&#39;../images/unchecked-checkbox.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;        width: 24px;&lt;br /&gt;        height: 24px;&lt;br /&gt;      }&lt;br /&gt;      :host([hidden]) {&lt;br /&gt;        display: none;&lt;br /&gt;      }&lt;br /&gt;      :host([checked]) {&lt;br /&gt;        background: url(&#39;../images/checked-checkbox.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;      }&lt;br /&gt;      :host([disabled]) {&lt;br /&gt;        background:&lt;br /&gt;          url(&#39;../images/unchecked-checkbox-disabled.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;      }&lt;br /&gt;      :host([checked][disabled]) {&lt;br /&gt;        background:&lt;br /&gt;          url(&#39;../images/checked-checkbox-disabled.svg&#39;) no-repeat;&lt;br /&gt;        background-size: contain;&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/style&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;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowToCheckbox&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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observedAttributes&lt;/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;br /&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 string&quot;&gt;&#39;checked&#39;&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The element&#39;s constructor is run anytime a new instance is created. Instances are created either by parsing HTML, calling document.createElement(&#39;howto-checkbox&#39;), or calling new HowToCheckbox(); The constructor is a good place to create shadow DOM, though you should avoid touching any attributes or light DOM children as they may not be available yet.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;br /&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;br /&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;attachShadow&lt;/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 literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;open&#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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot&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;template&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 class=&quot;token function&quot;&gt;cloneNode&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;connectedCallback()&lt;/code&gt; fires when the element is inserted into the DOM. It&#39;s a good place to set the initial &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;tabindex&lt;/code&gt;, internal state, and install event listeners.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/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;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;checkbox&#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;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&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;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A user may set a property on an instance of an element, before its prototype has been connected to this class. The &lt;code&gt;_upgradeProperty()&lt;/code&gt; method will check for any instance properties and run them through the proper class setters. See the &lt;a href=&quot;https://web.dev/custom-elements-best-practices/#make-properties-lazy&quot;&gt;lazy properties&lt;/a&gt; section for more details.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#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;br /&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;_upgradeProperty&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&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;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;keyup&#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;_onKeyUp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prop&lt;/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;br /&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 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;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prop&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;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &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;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;delete&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;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;disconnectedCallback()&lt;/code&gt; fires when the element is removed from the DOM. It&#39;s a good place to do clean up work like releasing references and removing event listeners.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;disconnectedCallback&lt;/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;br /&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;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;keyup&#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;_onKeyUp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;removeEventListener&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Properties and their corresponding attributes should mirror one another. The property setter for checked handles truthy/falsy values and reflects those to the state of the attribute. See the &lt;a href=&quot;https://web.dev/custom-elements-best-practices/#avoid-reentrancy-issues&quot;&gt;avoid reentrancy&lt;/a&gt; section for more details.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isChecked &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isChecked&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#39;&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br /&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;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checked&lt;/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;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;checked&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isDisabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isDisabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;setAttribute&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 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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br /&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;removeAttribute&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;disabled&lt;/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;br /&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;hasAttribute&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;attributeChangedCallback()&lt;/code&gt; is called when any of the attributes in the observedAttributes array are changed. It&#39;s a good place to handle side effects, like setting ARIA attributes.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;attributeChangedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; oldValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hasValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newValue &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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;checked&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-checked&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;disabled&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-disabled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;tabindex&lt;/code&gt; attribute does not provide a way to fully remove focusability from an element. Elements with &lt;code&gt;tabindex=-1&lt;/code&gt; can still be focused with a mouse or by calling &lt;code&gt;focus()&lt;/code&gt;. To make sure an element is disabled and not focusable, remove the &lt;code&gt;tabindex&lt;/code&gt; attribute.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;hasValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&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;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the focus is currently on this element, unfocus it by calling the &lt;code&gt;HTMLElement.blur()&lt;/code&gt; method&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;blur&lt;/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;br /&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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0&#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;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;_onKeyUp&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Don’t handle modifier shortcuts typically used by assistive technology.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;altKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SPACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&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;br /&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;_toggleChecked&lt;/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;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Any other key press is ignored and passed back to the browser.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&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;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;_onClick&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;br /&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;_toggleChecked&lt;/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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_toggleChecked()&lt;/code&gt; calls the checked setter and flips its state. Because &lt;code&gt;_toggleChecked()&lt;/code&gt; is only caused by a user action, it will also dispatch a change event. This event bubbles in order to mimic the native behavior of &lt;code&gt;&amp;lt;input type=checkbox&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_toggleChecked&lt;/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;br /&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;checked &lt;span class=&quot;token operator&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;checked&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;dispatchEvent&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;CustomEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;change&#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;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;detail&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;br /&gt;          &lt;span class=&quot;token literal-property property&quot;&gt;checked&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;checked&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;bubbles&lt;/span&gt;&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;br /&gt;      &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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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;howto-checkbox&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowToCheckbox&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – howto-tabs</title>
    <link href="https://web.dev/components-howto-tabs/"/>
    <updated>2017-04-06T00:00:00Z</updated>
    <id>https://web.dev/components-howto-tabs/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; limit visible content by separating it into multiple panels. Only
one panel is visible at a time, while &lt;em&gt;all&lt;/em&gt; corresponding tabs are always
visible. To switch from one panel to another, the corresponding tab has to be
selected.&lt;/p&gt;
&lt;p&gt;By either clicking or by using the arrow keys the user can change the
selection of the active tab.&lt;/p&gt;
&lt;p&gt;If JavaScript is disabled, all panels are shown interleaved with the
respective tabs. The tabs now function as headings.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/howto-components&quot; rel=&quot;noopener&quot;&gt;HowTo: Components on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel&quot; rel=&quot;noopener&quot;&gt;Tabs pattern in ARIA Authoring Practices 1.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://googlechromelabs.github.io/howto-components/howto-tabs/#demo&quot; rel=&quot;noopener&quot;&gt;View live demo on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;example-usage&quot;&gt;Example usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#example-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  howto-tab {&lt;br /&gt;    border: 1px solid black;&lt;br /&gt;    padding: 20px;&lt;br /&gt;  }&lt;br /&gt;  howto-panel {&lt;br /&gt;    padding: 20px;&lt;br /&gt;    background-color: lightgray;&lt;br /&gt;  }&lt;br /&gt;  howto-tab[selected] {&lt;br /&gt;    background-color: bisque;&lt;br /&gt;  }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If JavaScript does not run, the element will not match &lt;code&gt;:defined&lt;/code&gt;. In that case this style adds spacing between tabs and previous panel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;  howto-tabs:not(:defined), howto-tab:not(:defined), howto-panel:not(:defined) {&lt;br /&gt;    display: block;&lt;br /&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&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;howto-tabs&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tab&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;Tab 1&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;howto-tab&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-panel&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;panel&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;Content 1&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;howto-panel&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tab&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;Tab 2&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;howto-tab&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-panel&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;panel&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;Content 2&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;howto-panel&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tab&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;Tab 3&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;howto-tab&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;howto-panel&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;panel&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;Content 3&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;howto-panel&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;howto-tabs&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&gt;&lt;h2 id=&quot;code&quot;&gt;Code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tabs/#code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Define key codes to help with handling keyboard events.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&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;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token constant&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To avoid invoking the parser with &lt;code&gt;.innerHTML&lt;/code&gt; for every new instance, a template for the contents of the shadow DOM is shared by all &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; instances.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &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;template&#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;br /&gt;  template&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;    &amp;lt;style&gt;&lt;br /&gt;      :host {&lt;br /&gt;        display: flex;&lt;br /&gt;        flex-wrap: wrap;&lt;br /&gt;      }&lt;br /&gt;      ::slotted(howto-panel) {&lt;br /&gt;        flex-basis: 100%;&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/style&gt;&lt;br /&gt;    &amp;lt;slot name=&quot;tab&quot;&gt;&amp;lt;/slot&gt;&lt;br /&gt;    &amp;lt;slot name=&quot;panel&quot;&gt;&amp;lt;/slot&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;HowtoTabs is a container element for tabs and panels.&lt;/p&gt;
&lt;p&gt;All children of &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; should be either &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;howto-tabpanel&amp;gt;&lt;/code&gt;. This element is stateless, meaning that no values are cached and therefore, changes during runtime work.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoTabs&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;br /&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;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Event handlers that are not attached to this element need to be bound if they need access to &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;_onSlotChange &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;_onSlotChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For progressive enhancement, the markup should alternate between tabs and panels. Elements that reorder their children tend to not work well with frameworks. Instead shadow DOM is used to reorder the elements by using slots.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;attachShadow&lt;/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 literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;open&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Import the shared template to create the slots for tabs and panels.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;shadowRoot&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;template&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 class=&quot;token function&quot;&gt;cloneNode&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tabSlot &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;shadowRoot&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;slot[name=tab]&#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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_panelSlot &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;shadowRoot&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;slot[name=panel]&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This element needs to react to new children as it links up tabs and panel semantically using &lt;code&gt;aria-labelledby&lt;/code&gt; and &lt;code&gt;aria-controls&lt;/code&gt;. New children will get slotted automatically and cause slotchange to fire, so not MutationObserver is needed.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;_tabSlot&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;slotchange&#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;_onSlotChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_panelSlot&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;slotchange&#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;_onSlotChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;connectedCallback()&lt;/code&gt; groups tabs and panels by reordering and makes sure exactly one tab is active.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The element needs to do some manual input event handling to allow switching with arrow keys and &lt;kbd&gt;Home&lt;/kbd&gt; / &lt;kbd&gt;End&lt;/kbd&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&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;keydown&#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;_onKeyDown&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tablist&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Up until recently, &lt;code&gt;slotchange&lt;/code&gt; events did not fire when an element was upgraded by the parser. For this reason, the element invokes the handler manually. Once the new behavior lands in all browsers, the code below can be removed.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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 class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;        customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;whenDefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-tab&#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;br /&gt;        customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;whenDefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;howto-panel&#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;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&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;_linkPanels&lt;/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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;disconnectedCallback()&lt;/code&gt; removes the event listeners that &lt;code&gt;connectedCallback()&lt;/code&gt; added.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;disconnectedCallback&lt;/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;br /&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;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;keydown&#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;_onKeyDown&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;removeEventListener&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_onSlotChange()&lt;/code&gt; is called whenever an element is added or removed from one of the shadow DOM slots.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_onSlotChange&lt;/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;br /&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;_linkPanels&lt;/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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_linkPanels()&lt;/code&gt; links up tabs with their adjacent panels using aria-controls and &lt;code&gt;aria-labelledby&lt;/code&gt;. Additionally, the method makes sure only one tab is active.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_linkPanels&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tabs &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;_allTabs&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Give each panel a &lt;code&gt;aria-labelledby&lt;/code&gt; attribute that refers to the tab that controls it.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      tabs&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tab&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;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; panel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nextElementSibling&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/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; &lt;span class=&quot;token string&quot;&gt;&#39;howto-panel&#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;br /&gt;          console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&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;Tab #&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; is not a&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&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;sibling of a &amp;lt;howto-panel&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;br /&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;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-controls&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-labelledby&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The element checks if any of the tabs have been marked as selected. If not, the first tab is now selected.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; selectedTab &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;        tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/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;tab&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; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; tabs&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;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, switch to the selected tab. &lt;code&gt;_selectTab()&lt;/code&gt; takes care of marking all other tabs as deselected and hiding all other panels.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedTab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_allPanels()&lt;/code&gt; returns all the panels in the tab panel. This function could memorize the result if the DOM queries ever become a performance issue. The downside of memorization is that dynamically added tabs and panels will not be handled.&lt;/p&gt;
&lt;p&gt;This is a method and not a getter, because a getter implies that it is cheap to read.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_allPanels&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&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;&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;howto-panel&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_allTabs()&lt;/code&gt; returns all the tabs in the tab panel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_allTabs&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&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;&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;howto-tab&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_panelForTab()&lt;/code&gt; returns the panel that the given tab controls.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_panelForTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tab&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; panelId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tab&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;aria-controls&#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;br /&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;querySelector&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;#&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;panelId&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_prevTab()&lt;/code&gt; returns the tab that comes before the currently selected one, wrapping around when reaching the first one.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_prevTab&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tabs &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;_allTabs&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Use &lt;code&gt;findIndex()&lt;/code&gt; to find the index of the currently selected element and subtracts one to get the index of the previous element.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&lt;/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;tab&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; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected&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;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Add &lt;code&gt;tabs.length&lt;/code&gt; to make sure the index is a positive number and get the modulus to wrap around if necessary.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newIdx &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_firstTab()&lt;/code&gt; returns the first tab.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_firstTab&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tabs &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;_allTabs&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; tabs&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;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_lastTab()&lt;/code&gt; returns the last tab.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_lastTab&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tabs &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;_allTabs&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;tabs&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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_nextTab()&lt;/code&gt; gets the tab that comes after the currently selected one, wrapping around when reaching the last tab.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_nextTab&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tabs &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;_allTabs&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&lt;/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;tab&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; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected&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;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;newIdx &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; tabs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;reset()&lt;/code&gt; marks all tabs as deselected and hides all the panels.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;reset&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tabs &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;_allTabs&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; panels &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;_allPanels&lt;/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;br /&gt;&lt;br /&gt;      tabs&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tab&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; tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected &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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      panels&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;panel&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; panel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_selectTab()&lt;/code&gt; marks the given tab as selected. Additionally, it unhides the panel corresponding to the given tab.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;newTab&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Deselect all tabs and hide all panels.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;reset&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Get the panel that the &lt;code&gt;newTab&lt;/code&gt; is associated with.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newPanel &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;_panelForTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If that panel doesn’t exist, abort.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;newPanel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&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;No panel with id &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newPanelId&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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      newTab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selected &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;br /&gt;      newPanel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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;br /&gt;      newTab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_onKeyDown()&lt;/code&gt; handles key presses inside the tab panel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_onKeyDown&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the keypress did not originate from a tab element itself, it was a keypress inside the a panel or on empty space. Nothing to do.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&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;role&#39;&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;tab&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Don’t handle modifier shortcuts typically used by assistive technology.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;altKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The switch-case will determine which tab should be marked as active depending on the key that was pressed.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newTab&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LEFT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &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;_prevTab&lt;/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;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RIGHT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DOWN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &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;_nextTab&lt;/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;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HOME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &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;_firstTab&lt;/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;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;KEYCODE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          newTab &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;_lastTab&lt;/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;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Any other key press is ignored and passed back to the browser.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&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;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The browser might have some native functionality bound to the arrow keys, home or end. The element calls &lt;code&gt;preventDefault()&lt;/code&gt; to prevent the browser from taking any actions.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Select the new tab, that has been determined in the switch-case.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;_onClick()&lt;/code&gt; handles clicks inside the tab panel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_onClick&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the click was not targeted on a tab element itself, it was a click inside the a panel or on empty space. Nothing to do.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&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;role&#39;&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;tab&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If it was on a tab element, though, select that tab.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;_selectTab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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;howto-tabs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoTabs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;howtoTabCounter&lt;/code&gt; counts the number of &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; instances created. The number is used to generated new, unique IDs.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; howtoTabCounter &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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;HowtoTab&lt;/code&gt; is a tab for a &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; tab panel. &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; should always be used with &lt;code&gt;role=&amp;quot;heading&amp;quot;&lt;/code&gt; in the markup so that the semantics remain useable when JavaScript is failing.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; declares which &lt;code&gt;&amp;lt;howto-panel&amp;gt;&lt;/code&gt; it belongs to by using that panel’s ID as the value for the aria-controls attribute.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-tab&amp;gt;&lt;/code&gt; will automatically generate a unique ID if none is specified.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoTab&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;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observedAttributes&lt;/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;br /&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 string&quot;&gt;&#39;selected&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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;br /&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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If this is executed, JavaScript is working and the element changes its role to &lt;code&gt;tab&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tab&#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;br /&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;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &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;howto-tab-generated-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;howtoTabCounter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Set a well-defined initial state.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-selected&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;false&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&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 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;br /&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;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Check if a property has an instance value. If so, copy the value, and delete the instance property so it doesn&#39;t shadow the class property setter. Finally, pass the value to the class property setter so it can trigger any side effects. This is to safe guard against cases where, for instance, a framework may have added the element to the page and set a value on one of its properties, but lazy loaded its definition. Without this guard, the upgraded element would miss that property and the instance property would prevent the class property setter from ever being called.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;_upgradeProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prop&lt;/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;br /&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 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;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prop&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;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &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;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;delete&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;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Properties and their corresponding attributes should mirror one another. To this effect, the property setter for &lt;code&gt;selected&lt;/code&gt; handles truthy/falsy values and reflects those to the state of the attribute. It’s important to note that there are no side effects taking place in the property setter. For example, the setter does not set &lt;code&gt;aria-selected&lt;/code&gt;. Instead, that work happens in the &lt;code&gt;attributeChangedCallback&lt;/code&gt;. As a general rule, make property setters very dumb, and if setting a property or attribute should cause a side effect (like setting a corresponding ARIA attribute) do that work in the &lt;code&gt;attributeChangedCallback()&lt;/code&gt;. This will avoid having to manage complex attribute/property reentrancy scenarios.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;attributeChangedCallback&lt;/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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value &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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;aria-selected&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&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 operator&quot;&gt;:&lt;/span&gt; &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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&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;br /&gt;      value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#39;&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;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br /&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;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selected&lt;/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;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;selected&#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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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;howto-tab&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoTab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; howtoPanelCounter &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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;HowtoPanel&lt;/code&gt; is a panel for a &lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt; tab panel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoPanel&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;br /&gt;&lt;br /&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;br /&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;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tabpanel&#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;br /&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;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &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;howto-panel-generated-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;howtoPanelCounter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&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;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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;howto-panel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoPanel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – howto-tooltip</title>
    <link href="https://web.dev/components-howto-tooltip/"/>
    <updated>2017-04-06T00:00:00Z</updated>
    <id>https://web.dev/components-howto-tooltip/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;howto-tooltip&amp;gt;&lt;/code&gt; is a popup that displays information related to an element
when the element receives keyboard focus or the mouse hovers over it.
The element that triggers the tooltip references the tooltip element with
&lt;code&gt;aria-describedby&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The element self-applies the role &lt;code&gt;tooltip&lt;/code&gt; and sets &lt;code&gt;tabindex&lt;/code&gt; to -1, as the
tooltip itself can never be focused.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/howto-components&quot; rel=&quot;noopener&quot;&gt;HowTo: Components on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/#tooltip&quot; rel=&quot;noopener&quot;&gt;Tooltip pattern in ARIA Authoring Practices 1.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://googlechromelabs.github.io/howto-components/howto-tooltip/#demo&quot; rel=&quot;noopener&quot;&gt;View live demo on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;example-usage&quot;&gt;Example usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#example-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div&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;div&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 attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&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;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;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&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;Your name:&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;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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tp1&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;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;howto-tooltip&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tp1&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;Ideally your name is Batman&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;howto-tooltip&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&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 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 attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cheese&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;Favourite type of cheese: &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;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;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cheese&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tp2&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;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;howto-tooltip&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tp2&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;Help I am trapped inside a tooltip message&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;howto-tooltip&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&gt;&lt;h2 id=&quot;code&quot;&gt;Code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-howto-tooltip/#code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HowtoTooltip&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The constructor does work that needs to be executed exactly once.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;These functions are used in a bunch of places, and always need to bind the correct this reference, so do it once.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;_show &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;_show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hide &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;_hide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;connectedCallback()&lt;/code&gt; fires when the element is inserted into the DOM. It&#39;s a good place to set the initial role, tabindex, internal state, and install event listeners.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/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;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;tooltip&#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;br /&gt;&lt;br /&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;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#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;br /&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;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&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 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;br /&gt;&lt;br /&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;_hide&lt;/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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The element that triggers the tooltip references the tooltip element with &lt;code&gt;aria-describedby&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;_target &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;[aria-describedby=&#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;id &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 class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;_target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The tooltip needs to listen to focus/blur events from the target, as well as hover events over the target.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;_target&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;focus&#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;_show&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&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;blur&#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;_hide&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&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;mouseenter&#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;_show&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&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;mouseleave&#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;_hide&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;disconnectedCallback()&lt;/code&gt; unregisters the event listeners that were set up in &lt;code&gt;connectedCallback()&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;disconnectedCallback&lt;/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;br /&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;_target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Remove the existing listeners, so that they don&#39;t trigger even though there&#39;s no tooltip to show.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&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;_target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&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;focus&#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;_show&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&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;blur&#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;_hide&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&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;mouseenter&#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;_show&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&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;mouseleave&#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;_hide&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_target &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;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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 punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;_hide&lt;/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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &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;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&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;howto-tooltip&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HowtoTooltip&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
  
  <entry>
    <title>HowTo Components – Overview</title>
    <link href="https://web.dev/components-examples-overview/"/>
    <updated>2017-04-06T00:00:00Z</updated>
    <id>https://web.dev/components-examples-overview/</id>
    <content type="html" mode="escaped">&lt;p&gt;&amp;quot;HowTo: Components&amp;quot; are a collection of web components that implement common UI
patterns. The purpose of these implementations is to be an educational resource.
You can read through the densely commented implementation of different
components and hopefully learn from them. Note that they are explicitly &lt;strong&gt;NOT&lt;/strong&gt;
a UI library and should &lt;strong&gt;NOT&lt;/strong&gt; be used in production.&lt;/p&gt;
&lt;h2 id=&quot;components&quot;&gt;Components &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#components&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/components-howto-checkbox/&quot;&gt;&lt;code&gt;&amp;lt;howto-checkbox&amp;gt;&lt;/code&gt;&lt;/a&gt;: represents a boolean option in a form. The most common type of
checkbox is a dual-type which allows the user to toggle between two choices—checked and unchecked.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/components-howto-tabs/&quot;&gt;&lt;code&gt;&amp;lt;howto-tabs&amp;gt;&lt;/code&gt;&lt;/a&gt;: limits visible content by separating it into multiple panels.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/components-howto-tooltip/&quot;&gt;&lt;code&gt;&amp;lt;howto-tooltip&amp;gt;&lt;/code&gt;&lt;/a&gt;: a popup that displays information related to an element when the element
receives keyboard focus or the mouse hovers over it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;goals&quot;&gt;Goals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#goals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our aim is to demonstrate best practices for writing robust components that are
accessible, performant, maintainable, and easy to style. Each component is
completely self-contained so it can serve as a reference implementation.&lt;/p&gt;
&lt;h3 id=&quot;accessibility&quot;&gt;Accessibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#accessibility&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The components closely follow the &lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.1/&quot; rel=&quot;noopener&quot;&gt;WAI ARIA Authoring
Practices&lt;/a&gt;, which is a guide to
explain and show ARIA, the &lt;a href=&quot;https://www.w3.org/TR/wai-aria-1.1/&quot; rel=&quot;noopener&quot;&gt;Accessible Rich Internet Application
standard&lt;/a&gt;. If you are unfamiliar with ARIA,
&lt;a href=&quot;https://web.dev/semantics-aria/&quot;&gt;check out our introduction on
WebFundamentals&lt;/a&gt;.
Each component links to the relevant section of the Authoring Practices. While
not strictly necessary, we do recommend reading the section of the Authoring
Practices before diving into the code.&lt;/p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In web development the term &amp;quot;performance&amp;quot; can be applied to a multitude of
things. In the context of &lt;code&gt;&amp;lt;howto&amp;gt;&lt;/code&gt;, performance mostly refers to animations
consistently running at 60fps, even on mobile devices.&lt;/p&gt;
&lt;h3 id=&quot;visual-flexibility&quot;&gt;Visual Flexibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#visual-flexibility&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As much as possible, components are not styled, except for layout or to indicate
a selected or active state. This is to keep the implementation visually flexible
and focused. By not spending time on decoration, we limit the code to only what
is absolutely necessary to make the component function. If any style is required
for the component to function, the style will be marked with a comment
explaining why that is.&lt;/p&gt;
&lt;h3 id=&quot;maintainable-code&quot;&gt;Maintainable code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#maintainable-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As HowTo: Components is a reference
implementation, we spent extra time on writing readable and easily
comprehensible code that is densely commented.&lt;/p&gt;
&lt;h2 id=&quot;non-goals&quot;&gt;Non-Goals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#non-goals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;be-a-library-framework-toolkit&quot;&gt;Be a library / framework / toolkit &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#be-a-library-framework-toolkit&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;howto&amp;gt;&lt;/code&gt; components are not published on npm, bower or any other platform
because they are not meant to be used in production. For the sake of terse,
readable code, we are using modern JavaScript APIs and are supporting modern
browsers which implement the Web Components standards. You
will be able to adapt the code to fit your own needs after reading these
implementations.&lt;/p&gt;
&lt;h3 id=&quot;be-backwards-compatible&quot;&gt;Be backwards compatible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#be-backwards-compatible&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The code should not be relied on directly. We might, and very likely &lt;em&gt;will&lt;/em&gt;,
drastically change the implementation and API of any element if a better
implementation is discovered. This is a living resource where we can share,
explore, and discuss best practices for building web UIs.&lt;/p&gt;
&lt;h3 id=&quot;be-complete&quot;&gt;Be complete &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/components-examples-overview/#be-complete&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We currently don&#39;t (and probably won&#39;t) implement *&lt;em&gt;all&lt;/em&gt; *components that can be
found in the WAI ARIA Authoring Practices. However, re-using the principles used
in other &lt;code&gt;&amp;lt;howto&amp;gt;&lt;/code&gt; components should enable readers to implement any components
that are missing.&lt;/p&gt;
</content>
    <author>
      <name>Ewa Gasperowicz</name>
    </author><author>
      <name>Rob Dodson</name>
    </author><author>
      <name>Surma</name>
    </author>
  </entry>
</feed>
